Plot list (for 3D plots scroll down from the legend; you can rotate and zoom the 3D plots with your mouse):
Volume Category Histograms | Where muons die (coffee ring plots) | Muon ranging out in iron yoke | Muon scatters in inflector | Distance distribution in Ring iron | Energy loss in volumes | Number of steps in volumes | Another muon in the iron | Stored muon | Coherent Betatron Oscillation

1 Introduction

The goal of this document is to examine “escaped muons” that do not decay in the storage region and do not enter a calorimeter. Some of these muons may leave the storage ring altogether.

I generated art files of simulated events using mdc2 with different magnetic fields turned on or off (scenarios). Note: These files are still not the latest version of MDC2. Nathan is tuning up the injection gun. So right now the storage efficiency I see (0.5%) is still low. Should get better with the next round. We want to examine,

# Load necessary libraries
library(reticulate)  # Access to python
library(stringr)   # string functions
library(parallel)  # Parallel processing (built-in to R)
library(dplyr)     # data analysis
library(purrr)     # Functional programming
library(readr)     # Read formats
library(ggplot2)   # ploting
library(rgl)       # 3D plots
library(gridExtra) # Arranging plots
library(testthat)  # testing
#
library(readGallery) # Read art Gallery files
# Put all readGallery::useDataProduct calls here
useDataProduct('std::vector<gm2truth::TrackingActionArtRecord>')
useDataProduct('std::vector<gm2ringsim::GeantTrackRecord>')
useDataProduct('std::vector<gm2truth::GhostDetectorArtRecord>')
# To restore most of the environment
trackingActionDF <- read_rds('trackingActionDF.rds')
trackingActionNDF <- read_rds('trackingActionNDF.rds')
volNameDF <- read_rds('volNameDF.rds')
ghCylDF <- read_rds('ghCylDF.rds')
ghWldDF <- read_rds('ghWldDF.rds')

2 Load the samples

Samples are located on the Fermilab dCache in the directory /pnfs/GM2/scratch/users/lyon/arr_20170321.

# Function to properly alter paths to accomodate XRootD
#    e.g. convert /pnfs/BLA --> root://fndca1.fnal.gov/pnfs/fnal.gov/usr/BLA
xrootd_ify <- function(aPath) paste0('root://fndca1.fnal.gov/pnfs/fnal.gov/usr', str_replace(aPath, '/pnfs', ''))

Determine the locations of the data files

# Determine locations of our files
system("ifdh ls '/pnfs/GM2/scratch/users/lyon/arr_20170321/*/*.root' | grep ARR", intern=T) %>% sort() -> arrFiles
arrFiles %>% xrootify() -> arrXrdFiles
arrXrdFiles
 [1] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root"   
 [2] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243526_0/ARR_unified_noDipole_cyl.root"     
 [3] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243527_0/ARR_unified_noInflector_cyl.root"  
 [4] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243528_0/ARR_unified_noKickerQuads_cyl.root"
 [5] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243530_0/ARR_unified_noKicker_cyl.root"     
 [6] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243532_0/ARR_unified_noQuads_cyl.root"      
 [7] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243540_0/ARR_unified_everything_cyl.root"   
 [8] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243542_0/ARR_unified_noDipole_cyl.root"     
 [9] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243544_0/ARR_unified_noInflector_cyl.root"  
[10] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243547_0/ARR_unified_noKickerQuads_cyl.root"
[11] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243548_0/ARR_unified_noKicker_cyl.root"     
[12] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243552_0/ARR_unified_noQuads_cyl.root"      
write_rds(arrFiles, 'arrFiles.rds')

Pull out the scenaios (these will be in the same order as the file names)

scenarios <- str_match(arrFiles, 'arr_20170321/.+unified_(.+)_cyl')[,2]
scenarios %>% unique()
[1] "everything"    "noDipole"      "noInflector"   "noKickerQuads" "noKicker"      "noQuads"      

Let’s look at the contents of these files (they’re all the same, so we’ll just do one) gm2 v7_04_00 will have product_sizes_dumper.

stringToRun <- str_interp(
  "ssh lyon@gm2gpvm04.fnal.gov 'source /cvmfs/gm2.opensciencegrid.org/prod7/g-2/setup
   setup gm2 v7_04_00 -q prof ; 
   product_sizes_dumper ${aFile} | grep ::'", 
  list(aFile = arrFiles[1] %>% str_replace('fnal.gov/usr/',''))
)
#
system(stringToRun)
        1410762186      0.456 gm2truth::MagnetIronAndCryoArtRecords_artg4_magnetIronAndCryostat_mdc2arr.
         660570363      0.214 gm2truth::TrackingActionArtRecords_artg4__mdc2arr.
         637307490      0.206 gm2truth::TrajectoryArtRecords_artg4__mdc2arr.
         240738334      0.078 gm2ringsim::GeantTrackRecords_artg4_KeepStepsAction_mdc2arr.
         118035827      0.038 gm2truth::RingArtRecords_artg4_Ring_mdc2arr.
          12078805      0.004 gm2truth::GhostDetectorArtRecords_artg4_GhostCylinderDetector_mdc2arr.
           4482185      0.001 art::RNGsnapshots_randomsaver__mdc2arr.
           4086898      0.001 gm2truth::RingTrackingPlaneArtRecords_artg4_RingTrackingPlanes_mdc2arr.
           1373178      0.000 gm2truth::GhostDetectorArtRecords_artg4_GhostNearWorldDetector_mdc2arr.
           1185772      0.000 gm2truth::PhaseSpaceArtRecord_artg4__mdc2arr.
             36971      0.000 art::TriggerResults_TriggerResults__mdc2arr.

3 Escaping muons from the Tracking Action data

The tracking action data has birth and death data for every Geant track.

3.1 Load Tracking Action data

We’ll capture this information for the primary muon and its first generation daughters. See gm2dataproducts/mc/actions/track/TrackingActionArtRecord.hh.

We need a readGallery reader python class. Generate with,

readerClassSkel('gm2truth::TrackingActionArtRecord', writeFile = 'trackingActionReader.py')

Here is the reader,

read_file('trackingActionReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class TrackingActionArtRecordReader(GalleryReaderBase):
  def __init__(self, inputTag):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackType', 'trackID', 
                  'parentTrackID', 'turn', 'volumeUID', 'status',  'p', 'e', 'x', 'y', 'z', 'px', 'py', 'pz']

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.TrackingActionArtRecord))

  def fill(self, ROOT, ev):

    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for 
                                                      # gm2truth::TrackingActionArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::TrackingActionArtRecord
      for e in p:                             # Loop over elements and fill
        
        # Only accept the primary muon death and its first generation daughters birth
        if (e.trackID == 1 and e.status == 1) or (e.parentTrackID == 1 and e.status == 0):
        
          self.vals.append(
            [ ev.fileEntry(), ev.eventEntry(), e.trackType, e.trackID, 
              e.parentTrackID, e.turn, e.volumeUID, e.status, e.p, e.e, 
              e.x, e.y, e.z, e.px, e.py, e.pz ])

    return True

Make the reader class and object

trackingActionReaderC <- createReaderClass_from_file('trackingActionReader.py')$TrackingActionArtRecordReader

We have many files to process. Let’s do this reading in parallel.

We need to create a reader object per parallel job

trackingActionReaders <- map(1:length(arrXrdFiles), function(i) trackingActionReaderC(artInputTag("artg4")))

Load the data

# For some reason these jobs seem to run serially
jobs <- lapply(1:length(arrXrdFiles), function(i) getGalleryData(arrXrdFiles[i], trackingActionReaders[[i]]) %>%
                                                    mcparallel(name=i))
trackingActionReturns <- mccollect(jobs)

Let’s merge all of the output.

trackingActionDF <- map_df(1:length(trackingActionReaders), 
                           function(i) galleryReader_df(trackingActionReaders[[i]]) %>% 
                             mutate(scenario=scenarios[i],fileEntry=i))
nrow(trackingActionDF)
[1] 488953
trackingActionDF %>% head(100)  # Let's not dump the entire data frame into the HTML page
# Write this out so we don't need to do the above again
write_rds(trackingActionDF, 'trackingActionDF.rds')

How many muons per file?

trackingActionDF %>% filter(trackID==1) %>% group_by(scenario, fileEntry) %>% tally()

3.1.1 Add Volume Names

We need to turn the volume IDs into volume names. The volume IDs change for each file. We can run a small art job to determine the volume ID to name tables.

runForVolIDString <- function(i) {
  str_interp(
    "PVS_CSVOUT=${csvout}_${i}_volNames.csv gm2 -c ${fclPath}/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 ${aFile}",
    list( csvout=scenarios[i], i=i, fclPath=Sys.getenv("MRB_BUILDDIR"), aFile=arrXrdFiles[i]) 
  )
}
runForVolIDStrings <- sapply(1:length(arrXrdFiles), runForVolIDString)
runForVolIDStrings
 [1] "PVS_CSVOUT=everything_1_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root"       
 [2] "PVS_CSVOUT=noDipole_2_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243526_0/ARR_unified_noDipole_cyl.root"           
 [3] "PVS_CSVOUT=noInflector_3_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243527_0/ARR_unified_noInflector_cyl.root"     
 [4] "PVS_CSVOUT=noKickerQuads_4_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243528_0/ARR_unified_noKickerQuads_cyl.root" 
 [5] "PVS_CSVOUT=noKicker_5_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243530_0/ARR_unified_noKicker_cyl.root"           
 [6] "PVS_CSVOUT=noQuads_6_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243532_0/ARR_unified_noQuads_cyl.root"             
 [7] "PVS_CSVOUT=everything_7_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243540_0/ARR_unified_everything_cyl.root"       
 [8] "PVS_CSVOUT=noDipole_8_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243542_0/ARR_unified_noDipole_cyl.root"           
 [9] "PVS_CSVOUT=noInflector_9_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243544_0/ARR_unified_noInflector_cyl.root"     
[10] "PVS_CSVOUT=noKickerQuads_10_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243547_0/ARR_unified_noKickerQuads_cyl.root"
[11] "PVS_CSVOUT=noKicker_11_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243548_0/ARR_unified_noKicker_cyl.root"          
[12] "PVS_CSVOUT=noQuads_12_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243552_0/ARR_unified_noQuads_cyl.root"            

Let’s run the art jobs in parallel

jobs <- lapply(1:length(arrXrdFiles), function(i) mcparallel(system( runForVolIDStrings[i], intern=T ), name=i))
res <- mccollect(jobs)

and load the CSV files.

jobs <- lapply(1:length(arrFiles), function(i) 
  str_interp("${scn}_${i}_volNames.csv", list(scn=scenarios[i], i=i)) %>%
    read_csv(col_names=c("volumeUID", "volName")) %>% mcparallel(name=i) )
#
volNameTables <- mccollect(jobs)
volNameDF <- map_df(1:length(volNameTables), function(i) 
                      volNameTables[[i]] %>% mutate(scenario=scenarios[i],
                                                    fileEntry=i))
nrow(volNameDF)
[1] 1014
volNameDF %>% head(100)
write_rds(volNameDF, 'volNameDF.rds')

Now we merge with the tracking action data frame

trackingActionDF %>% inner_join(volNameDF) %>% 
  select(scenario, fileEntry, eventEntry, volName, everything()) -> trackingActionNDF
Joining, by = c("fileEntry", "volumeUID", "scenario")
nrow(trackingActionNDF)
[1] 488953
trackingActionNDF %>% head(100)

3.2 How many muons decay in the storage region?

These muons decay in the storage region (not quite the same as “stored”),

trackingActionNDF %>% filter(trackID==1, volName=='ArcSection[00]') %>% 
  group_by(scenario, fileEntry) %>% tally()

Interesting! So, from this, only 0.575% decay in the storage region for the “all on” (or everything) sample. That seems rather low. But continuing.

write_rds(trackingActionNDF, 'trackingActionNDF.rds')

3.3 Where do muons go to die?

trackingActionNDF %>% filter(trackID == 1) %>% group_by(scenario, volName) %>% tally()

It may be nice to categorize the volume names by area (e.g. “Arc”, “bellows”, “Ring”, “Inflector”).

# Pull out the first word in camel case (see http://stackoverflow.com/questions/29916065/how-to-do-camelcase-split-in-python)
camelCaseSplit <- function(s) str_split(s, '(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])')
# Test the above
expect_equal(camelCaseSplit("RingPoleTipUpper")[[1]], c("Ring", "Pole", "Tip", "Upper"))
expect_equal(camelCaseSplit("ringPoleTipUpper")[[1]], c("ring", "Pole", "Tip", "Upper"))
expect_equal(camelCaseSplit("world")[[1]], c("world"))
expect_equal(camelCaseSplit("ringPole")[[1]], c("ring", "Pole"))
# We really just want the first word
takeFirstCamel <- function(s) camelCaseSplit(s) %>% map_chr(function(s) s[1])
#
# Test
takeFirstCamel("RingPoleTipUpper") %>% expect_equal("Ring")
takeFirstCamel("ringPoleTipUpper") %>% expect_equal("ring")
takeFirstCamel("world") %>% expect_equal("world")
takeFirstCamel("ringPole") %>% expect_equal("ring")
takeFirstCamel(c("ringPoleTipUpper", "world")) %>% expect_equal(c("ring", "world"))

Don’t confuse RingTrackingPlane with the iron (also starts with Ring)

makeVolCategory <- function(s) ifelse( str_detect(s, 'RingTracking'),
                                       "RngTrkPlane",
                                       takeFirstCamel(s))
makeVolCategory(c("RingBottom", "RingTrackingPlane[8]", "world")) %>% expect_equal(c("Ring", "RngTrkPlane", "world"))
trackingActionNDF %>% mutate(volCategory = volName %>% makeVolCategory()) -> trackingActionNVDF
trackingActionNVDF %>% head(100)

Show count by category

trackingActionNVDF %>% filter(trackID == 1) %>% group_by(scenario, volCategory) %>% tally() -> volCatTally
volCatTally %>% mutate(perc = n/20000 * 100) -> volCatTally
volCatTally

Let’s plot!

trackingActionNVDF %>% filter(trackID ==1) %>% ggplot(aes(x=volCategory, group=scenario)) + 
  geom_bar()

ggplot(volCatTally, aes(x = volCategory, y=perc, fill=scenario)) + 
  geom_bar(stat="Identity", width=0.5, position="dodge")

Can’t really see non-Ring/world ones. Let’s split them out.

# Function to make this easy
plotit <- function(d) ggplot(d, aes(x = volCategory, y=perc, fill=scenario) ) + 
  geom_bar(stat="Identity", width=0.5, position="dodge") + 
  xlab("Volume category") + ylab("Percent")
volCatTally %>% filter(volCategory %in% c("Ring", "world")) %>% plotit -> p1
volCatTally %>% filter(! volCategory %in% c("Ring", "world")) %>% plotit -> p2
grid.arrange(p1, p2)

Let’s see where they die

# See https://rpubs.com/hadley/97970 for how to wrap a multipart ggplot2 component
plotCommon <- function () {
  list(
    facet_wrap(~scenario),
    guides(col = guide_legend(override.aes = list(size=5, alpha=1))),
    scale_color_discrete(name="Volume\nCategory"),
    labs(x="z (mm)", y="x (mm)"),
    theme_minimal()
  )
}

The “Coffee Ring” plots

trackingActionNVDF %>% filter(trackID == 1) %>% 
  ggplot( aes(x=z, y=x, color=volCategory)) + 
    geom_point(alpha=0.1) + 
    ggtitle('Where Muons Die') +
    plotCommon()

Here it is without the ring losses, which dominate

trackingActionNVDF %>% filter(trackID == 1, volCategory != 'Ring') %>% 
  ggplot( aes(x=z, y=x, color=volCategory)) + 
    geom_point(alpha=0.5) + 
    ggtitle("Where Muons Die", subtitle = "Ring escapees excluded for clarity") +
    plotCommon()

3.4 Does it make sense for so many muons to stop in the iron?

We have 3 GeV muons. Does it make sense that the vast majority of them stop in the iron? A table shows the Continuous Slow Down Approximation range of muons (CSDA). For a 3 GeV muon in iron, the CSDA is \(1.825 \times 10^{3}\) g/cm\(^2\). The density \(\rho\) is 7.874 g/cm\(^3\). So, where \(R\) is range,

\[R = \text{CSDA}/\rho\] Range is thus 2.32 m. This is bigger than the width of the iron, but remember that the muons come in at an oblique angle. We’ll have to prove this.

4 How far do muons go in the iron?

We have the Geant stepping information, so we should be able to figure out how far the muons go in the iron.

Here is the Gallery reader,

read_file('GeantTrackReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class GeantStepReader(GalleryReaderBase):
  def __init__(self, inputTag, eventsToFill = []):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackID', 'globalStepNum', 'totalEnergyDeposit', 
                  'stepLength', 'volumeUID', 
                  'x', 'y', 'z', 'px', 'py', 'pz']
    self.eventsToFill = eventsToFill
    print self.eventsToFill

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2ringsim.GeantTrackRecord))

  def fill(self, ROOT, ev):

    # Do we care about this event - if not then don't bother looking?
    if self.eventsToFill and not ev.eventEntry() in self.eventsToFill:
      return True
      
    print 'Reading entry %s' % ev.eventEntry()

    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2ringsim::GeantTrackRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2ringsim::GeantStep
      for e in p:                             # Loop over elements and fill
      
        # Get the steps
        for f in e.steps():

          self.vals.append(
            [ ev.fileEntry(), ev.eventEntry(), e.trackID(), f.globalStepNum(), 
            f.totalEnergyDeposit(), f.stepLength(), f.volumeUID(), 
            f.pos()[0], f.pos()[1], f.pos()[2], 
            f.p()[0], f.p()[1], f.p()[2] ])
            
        # If we're on the last event, don't bother reading any more
        if self.eventsToFill and ev.eventEntry() == self.eventsToFill[-1]:
          return False

    return True
geantStepReaderC <- createReaderClass_from_file('GeantTrackReader.py')$GeantStepReader

4.1 Muon distance in iron for one event

Let’s find an event where the muon did in the iron.

trackingActionNVDF %>% filter(trackID == 1, volName == 'RingYokeBottom') %>% head(100)

Event 4 looks good.

geantStepOne <- geantStepReaderC(artInputTag('artg4:KeepStepsAction'), tuple(as.integer(4)))
(4,)
getGalleryData(arrXrdFiles[1], geantStepOne)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root
Reading entry 4
Timings: Overall time = 5.234 s
Time per event: min=0.000   mean=0.206   max=1.027
gsOneDF <- galleryReader_df(geantStepOne)

Merge in volume names

gsOneDF %>% mutate(fileEntry = 1) %>%
  inner_join(volNameDF) %>% 
  select(volName, everything()) -> gsOneDF
Joining, by = c("fileEntry", "volumeUID")
gsOneDF

Let’s make a 3D plot of this path

ring <- cylinder3d( center=rbind(c(0,-90, 0), c(0,90,0)),
                    radius=7112,
                    sides=20, closed = F)

Let’s do volume categories again.

gsOneDF %>% mutate(volCategory = volName %>% makeVolCategory %>% as.factor) -> gsOneDF

Let’s plot the path. Note that I have to make the legend separately since legend3d looks awful.

# lengend3d looks terrible - so do a regular legend
plot(1, type='n', axes=FALSE, ann=FALSE)
with(gsOneDF,
  legend("top", legend=unique(volCategory), col=as.integer(unique(volCategory)), pch=19)
)

clear3d()
with(gsOneDF,{
     plot3d(x=x, y=y, z=z, type='p', col = as.integer(volCategory), ylim=c(-400, 400))
})
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

How much energy loss and distance traveled in each volume category

gsOneDF %>% group_by(volCategory) %>% summarize(totalDist_meters=round(sum(stepLength)/1000, 2), 
                                                totalELoss_MeV=round(sum(totalEnergyDeposit), 1),
                                                nSteps = n())

Ok - I believe this (note that the y scale is very small compared to the x,z scales - so the muon acutally travels quite far in x & z). The muon travels about 2.4m in the iron and ranges out. Physics (and Geant) work!

Let’s try another one

geantStepOneA <- geantStepReaderC(artInputTag('artg4:KeepStepsAction'), tuple(as.integer(10)))
(10,)
getGalleryData(arrXrdFiles[1], geantStepOneA)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root
Reading entry 10
Timings: Overall time = 2.566 s
Time per event: min=0.000   mean=0.090   max=0.993
gsOneADF <- galleryReader_df(geantStepOneA)
gsOneADF %>% mutate(fileEntry = 1) %>%
  inner_join(volNameDF) %>% 
  mutate(volCategory = volName %>% makeVolCategory %>% as.factor)%>% 
  select(volName, volCategory, everything()) -> gsOneADF
Joining, by = c("fileEntry", "volumeUID")
gsOneADF
gsOneADF %>% group_by(volCategory) %>% summarize(totalDist_meters=round(sum(stepLength)/1000, 2), 
                                                totalELoss_MeV=round(sum(totalEnergyDeposit), 1),
                                                nSteps = n()) -> gsOneADFSum
gsOneADFSum

Check sums

gsOneADFSum %>% summarize(sum(totalELoss_MeV))
gsOneADF %>% summarize(sum(totalEnergyDeposit))

Sanity check passes!

# lengend3d looks terrible - so do a regular legend
plot(1, type='n', axes=FALSE, ann=FALSE)
with(gsOneADF,
  legend("top", legend=unique(volCategory), col=as.integer(unique(volCategory)), pch=19)
)

clear3d()
with(gsOneADF,{
     plot3d(x=x, y=y, z=z, type='p', col = as.integer(volCategory), ylim=c(-400, 400), xlim=c(-10000, 10000),
            zlim=c(-10000, 10000))
})
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

This one scatters in the inflector.

4.2 Muon distance for many events

Can we show how far muons go in various materials?

Let’s collect data for 100 events.

We have a reader that can do n events.

read_file('GeantTrackReaderN.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class GeantStepReaderN(GalleryReaderBase):
  def __init__(self, inputTag, startEvent=100, nEvents=10):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackID', 'globalStepNum', 'globalTime', 'totalEnergyDeposit', 
                  'stepLength', 'volumeUID', 
                  'x', 'y', 'z', 'px', 'py', 'pz']
    self.startEvent = startEvent
    self.endEvent   = startEvent + nEvents
    
  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2ringsim.GeantTrackRecord))

  def fill(self, ROOT, ev):

    # Do we care about this event - if not then don't bother looking
    if ev.eventEntry() < self.startEvent:
      return True
      
    if ev.eventEntry() >= self.endEvent:
      return False

    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2ringsim::GeantTrackRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2ringsim::GeantStep
      for e in p:                             # Loop over elements and fill
      
        # Get the steps
        for f in e.steps():

          self.vals.append(
            [ ev.fileEntry(), ev.eventEntry(), e.trackID(), f.globalStepNum(), f.globalTime(), 
            f.totalEnergyDeposit(), f.stepLength(), f.volumeUID(), 
            f.pos()[0], f.pos()[1], f.pos()[2], 
            f.p()[0], f.p()[1], f.p()[2] ])

    return True
geantStepReaderNC <- createReaderClass_from_file('GeantTrackReaderN.py')$GeantStepReaderN
geantStepN <- geantStepReaderNC(artInputTag('artg4:KeepStepsAction'), 1000, 1000)
getGalleryData(arrXrdFiles[1], geantStepN)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root
Timings: Overall time = 51.662 s
Time per event: min=0.000   mean=0.024   max=0.452
gsNDF <- galleryReader_df(geantStepN)
gsNDF %>% mutate(fileEntry = 1,
                 p = sqrt(px*px + py*py + pz*pz)) %>%
  inner_join(volNameDF) %>% 
  mutate(volCategory = volName %>% makeVolCategory %>% as.factor)%>% 
  select(volName, volCategory, p, everything()) -> gsNDF
Joining, by = c("fileEntry", "volumeUID")
nrow(gsNDF)
[1] 297501
gsNDF %>% head(100)

Let’s do a distribution of distance in the different volumes

gsNDF %>% group_by(eventEntry, volCategory) %>% summarize(
                                                totalDist_meters=round(sum(stepLength)/1000, 2), 
                                                totalELoss_MeV=round(sum(totalEnergyDeposit), 1),
                                                nSteps = n()) -> gsNDFSum
gsNDFSum

gsNDFSum %>% filter(volCategory == 'Ring') %>% 
  ggplot(aes(x=totalDist_meters)) + geom_histogram(bins=50) + 
    labs(x = 'Total distance in Ring material (m)', y='count', title='Muon in Ring Material')

Let’s look at this across the volume categories for energy loss

gsNDFSum %>% ggplot(aes(x=totalELoss_MeV)) + geom_histogram(bins=50) +
  facet_wrap(~volCategory, scales = "free") + 
  labs(x = 'Total energy loss (MeV)', y='count', title='Energy loss in material',
       subtitle = 'Note the different scales')

Let’s get an energy loss curve

gsNDF %>% ggplot(aes(x=p, y=totalEnergyDeposit)) + geom_point() + facet_wrap(~volCategory) + 
  labs(x="Muon Momentum (MeV/c)", y="Total Energy loss (MeV))")

Well, not sure what all this means. Let’s look for a particular event…

gsNDF %>% filter(eventEntry == 1300) %>% ggplot(aes(x=p, y=totalEnergyDeposit)) + geom_point() + facet_wrap(~volCategory) + 
  labs(x="Muon Momentum (MeV/c)", y="Total Energy loss (MeV))", title='Energy loss vs. Momentum for muons in event 1300' )

How many steps do we take?

gsNDFSum %>% ggplot(aes(x=nSteps)) + geom_histogram(bins=50) +
  facet_wrap(~volCategory, scales = "free") + 
  labs(x = 'Number of steps', y='count', title='Number of steps in materials',
       subtitle = 'Note the different scales')

Let’s look at two events

4.2.1 Muon with long distance in iron

gsNDFSum %>% filter(volCategory == 'Ring', totalDist_meters > 3)

Let’s look at event 1758

gsNDF %>% filter(eventEntry == 1758) -> gsNDF1758
gsNDF1758
gsNDF1758 %>% group_by(eventEntry, volCategory) %>% summarize(
                                                totalDist_meters=round(sum(stepLength)/1000, 2), 
                                                totalELoss_MeV=round(sum(totalEnergyDeposit), 1),
                                                nSteps = n()) -> gsNDF1758Sum
gsNDF1758Sum

Let’s plot it!

gsNDF1758 %>% mutate(volCategory = as.factor(as.character(volCategory))) -> gsNDF1758

# lengend3d looks terrible - so do a regular legend
plot(0, type='n', axes=FALSE, ann=FALSE)
with(gsNDF1758,
  legend("bottom", legend=unique(volCategory), col=as.integer(unique(volCategory)), pch=19)
)

clear3d()
with(gsNDF1758,{
     plot3d(x=x, y=y, z=z, type='p', col = as.integer(volCategory), ylim=c(-400, 400), xlim=c(-10000, 10000),
            zlim=c(-10000, 10000))
})
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

Looks like this one started out in a strange place.

4.2.2 Muon with many turns

gsNDFSum %>% filter(volCategory == 'Arc', totalDist_meters > 870)

Let’s try event 1509

gsNDF %>% filter(eventEntry == 1509) -> gsNDF1509
gsNDF1509
gsNDF1509 %>% group_by(eventEntry, volCategory) %>% summarize(
                                                totalDist_meters=round(sum(stepLength)/1000, 2), 
                                                totalELoss_MeV=round(sum(totalEnergyDeposit), 1),
                                                nSteps = n()) -> gsNDF1509Sum
gsNDF1509Sum

Let’s plot it!

gsNDF1509 %>% mutate(volCategory = as.factor(as.character(volCategory))) -> gsNDF1509

# lengend3d looks terrible - so do a regular legend
plot(0, type='n', axes=FALSE, ann=FALSE)
with(gsNDF1509,
  legend("bottom", legend=unique(volCategory), col=as.integer(unique(volCategory)), pch=19)
)

clear3d()
with(gsNDF1509,{
     plot3d(x=x, y=y, z=z, type='p', col = as.integer(volCategory), ylim=c(-400, 400), xlim=c(-10000, 10000),
            zlim=c(-10000, 10000))
})
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

Looks like a nice stored muon!! Can we see CBO?

gsNDF1509 %>% mutate(r = sqrt(x*x+z*z)) -> gsNDF1509

p1 <- gsNDF1509 %>% filter(volCategory %in% c('RngTrkPlane')) %>% ggplot(aes(x=globalTime, y=r)) + geom_line() +
  labs(x='Global time (ns)', y='Radius (mm)', title='Radial CBO', subtitle="Measured by Ring Tracking Planes")
#
p2 <- gsNDF1509 %>% filter(volCategory %in% c('Arc','RngTrkPlane')) %>% ggplot(aes(x=globalTime, y=r)) + 
  geom_line() + 
  labs(x='Global time (ns)', y='Radius (mm)', subtitle="Measured by Arc hits and Ring Tracking Planes")
#
grid.arrange(p1, p2)

We can even see the kick!

Here we’ll plot the points too.

p1 <- gsNDF1509 %>% filter(volCategory %in% c('RngTrkPlane')) %>% ggplot(aes(x=globalTime, y=y)) + geom_line() +
  geom_point() +
  labs(x='Global time (ns)', y='y (mm)', title='Vertical CBO', subtitle="Measured by Ring Tracking Planes")
#
p2 <- gsNDF1509 %>% filter(volCategory %in% c('Arc','RngTrkPlane')) %>% ggplot(aes(x=globalTime, y=y)) + 
  geom_line() + geom_point() + 
  labs(x='Global time (ns)', y='y (mm)', subtitle="Measured by Arc hits and Ring Tracking Planes")
#
grid.arrange(p1, p2)

5 “Ghost” cylinder at vacuum chamber

We want to examine muon escapees that leave the vacuum chamber. To measure this feature, we have a ghost cylinder at the outer wall of the vacuum chamber as well as above and below the storage region (before the muon would hit iron). That should catch every muon that doesn’t get stored and makes it out of the storage region. The data is gm2truth::GhostDetectorArtRecords_artg4_GhostCylinderDetector.

There is also a ghost cylinder just on the inside of the world cube to see muons that would actually leave the ring (and make it through the iron). The data is gm2truth::GhostDetectorArtRecords_artg4_GhostNearWorldDetector.

5.1 Load the Ghost cylinder data

See gm2dataproducts/mc/ghostdetectors/GhostDetectorArtRecord.hh .

readerClassSkel('gm2truth::GhostDetectorArtRecord', writeFile = 'GhostDetectorReader.py')
read_file('GhostDetectorReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class GhostDetectorArtRecordReader(GalleryReaderBase):
  def __init__(self, inputTag):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'particleID', 
                  'trackID', 'parentTrackID', 
                  'x', 'y', 'z', 
                  'px', 'py', 'pz', 
                  'sx', 'sy', 'sz', 'time', 'volumeName']

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.GhostDetectorArtRecord))

  def fill(self, ROOT, ev):

    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::GhostDetectorArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::GhostDetectorArtRecord
      
      for e in p:                             # Loop over elements and fill

        self.vals.append(
          [ ev.fileEntry(), ev.eventEntry(), e.particleID, e.trackID, e.parentTrackID, 
            e.position.x(), e.position.y(), e.position.z(), 
            e.momentum.x(), e.momentum.y(), e.momentum.z(), 
            e.spin.x(), e.spin.y(), e.spin.z(), e.time, e.volumeName ])

    return True
ghostDetectorReaderC <- createReaderClass_from_file('GhostDetectorReader.py')$GhostDetectorArtRecordReader

Let’s read both ghost cylinders

ghCylReader <- ghostDetectorReaderC(artInputTag('artg4:GhostCylinderDetector'))
ghWldReader <- ghostDetectorReaderC(artInputTag('artg4:GhostNearWorldDetector'))

Load for one file

getGalleryData(arrXrdFiles, c(ghCylReader, ghWldReader))
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243525_0/ARR_unified_everything_cyl.root
Closing file, read 16740159 bytes in 100 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243526_0/ARR_unified_noDipole_cyl.root
Closing file, read 11886702 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243527_0/ARR_unified_noInflector_cyl.root
Closing file, read 14680905 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243528_0/ARR_unified_noKickerQuads_cyl.root
Closing file, read 16282559 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243530_0/ARR_unified_noKicker_cyl.root
Closing file, read 16397132 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243532_0/ARR_unified_noQuads_cyl.root
Closing file, read 16580366 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243540_0/ARR_unified_everything_cyl.root
Closing file, read 16737803 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243542_0/ARR_unified_noDipole_cyl.root
Closing file, read 11850233 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243544_0/ARR_unified_noInflector_cyl.root
Closing file, read 14723528 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243547_0/ARR_unified_noKickerQuads_cyl.root
Closing file, read 16377209 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243548_0/ARR_unified_noKicker_cyl.root
Closing file, read 16397059 bytes in 66 transactions
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170321/15243552_0/ARR_unified_noQuads_cyl.root
Closing file, read 16814838 bytes in 66 transactions
Timings: Overall time = 252.037 s
Time per event: min=0.000   mean=0.000   max=7.044

Load the data into R

ghCylDF <- galleryReader_df(ghCylReader)
ghWldDF <- galleryReader_df(ghWldReader)

Look at what we got

nrow(ghCylDF) %>% format(big.mark=",")
[1] "2,374,697"
nrow(ghWldDF) %>% format(big.mark=",")
[1] "197,905"
ghCylDF %>% head(100)
ghWldDF %>% head(100)
write_rds(ghCylDF, 'ghCylDF.rds')  # Write out just in case, since these are expensive to extract
write_rds(ghWldDF, 'ghWldDF.rds')

Fill in the scenario and pdg

pdgs <- c('e-'= 11, 'nu_e'= 12, 'mu-'= 13, 'nu_mu'= 14, 'tau-'= 15, 'nu_tau'= 16,
          'e+'=-11, 'anti_nu_e'=-12, 'mu+'=-13, 'anti_nu_mu'=-14, 'tau+'=-15, 'anti_nu_tau'=-16,
          'gam'=22, 'p'=2212, 'n'=2112, 'anti-p'=-2212, 'anti-n'=-2112)
pdgs
         e-        nu_e         mu-       nu_mu        tau-      nu_tau          e+   anti_nu_e         mu+  anti_nu_mu 
         11          12          13          14          15          16         -11         -12         -13         -14 
       tau+ anti_nu_tau         gam           p           n      anti-p      anti-n 
        -15         -16          22        2212        2112       -2212       -2112 
# We need to be a little careful, since the scenario names are repeated (we have two files per scenario). 
# We can't make factor levels out of that. 
# The file entries with 0-5 are easy. 6-11 are harder
addScenarioFactor <- function(f) factor(if_else(f >= 6, as.integer(f-6), f), 
                                        levels=0:(length(unique(scenarios))-1), labels=unique(scenarios))
#
mutateThis <- function(df) mutate(df, 
                       scenario = addScenarioFactor(fileEntry),
                            pdg = factor(particleID, levels=pdgs, labels=names(pdgs)),
                              r = sqrt(x*x+z*z),
                            phi = atan2(z, x) %>% if_else(. > 0, ., 2*pi+ .) * (360.0/(2*pi)) )
#
ghCylDF %>% mutateThis -> ghCylDF
#
ghWldDF %>% mutateThis -> ghWldDF

5.2 Losses detected by the cylinder ghost detector

The “ghost cylinder” virtual detector in the parallel world sees muon escapees that hit the vacuum chamber outer wall. How many hits per muon do we see?

Pull out just the muon

ghCylDF %>% filter(trackID == 1) -> ghCylMuDF
nrow(ghCylMuDF) %>% format(big.mark=',')
[1] "132,158"

To do this right, we need to make a “key” out of the fileEntry and eventEntry columns.

ghCylMuDF %>% mutate( flevKey = unite ...  )
ghCylMuDF %>% group_by(fileEntry, eventEntry) %>% tally()

I’ve made 3D plots of all the scenarios (see below). Interesting to see what fraction get kicked up and down (are the ghost hits on the “ceiling or floor” or on the “wall”). We can tell by plotting the y coordinate…

ghCylDF %>% 
  ggplot(aes(y)) + geom_histogram(bins=50) + scale_y_log10() + facet_wrap(~scenario)

5.2.1 3D plots of all scenarios

Let’s practice with just the everything scenario

ghCylMu_everything_DF <- ghCylMuDF %>% filter(scenario == 'everything')
nrow(ghCylMu_everything_DF) %>% format(big.mark=',')
[1] "23,691"

Let’s plot where these are

plotEscapees <- function(i) {
  with( ghCylMuDF %>% filter(scenario == scenarios[i]),
   plot3d(x=x, y=y, z=z, xlim=c(-8000, 8000), ylim=c(-120, 120), zlim=c(-8000, 8000), main=scenarios[i]))
  plot3d(ring, add=T, alpha=0.2)
  view3d(phi=90, theta=-90)
}
clear3d()
plotEscapees(1)
rglwidget()

They’re all lost up or down, not along the side. Let’s try more of the scenarios.

clear3d()
plotEscapees(2)
rglwidget()

No storage ring dipole field: As we expect, the muons don’t go very far.

clear3d()
plotEscapees(3)
rglwidget()

No inflector field: Seemingly lots of muons kicked up and down (need to check that). Many slam into the wall.

clear3d()
plotEscapees(4)
rglwidget()
clear3d()
plotEscapees(5)
rglwidget()
clear3d()
plotEscapees(6)
rglwidget()

Well, this isn’t too illuminating.

LS0tCnRpdGxlOiAiRXNjYXBlZCBNdW9ucyIKZGF0ZTogMjAxNy0wMy0yMQphdXRob3I6IEFkYW0gTHlvbgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoqKlBsb3QgbGlzdCoqIChmb3IgM0QgcGxvdHMgc2Nyb2xsIGRvd24gZnJvbSB0aGUgbGVnZW5kOyB5b3UgY2FuIHJvdGF0ZSBhbmQgem9vbSB0aGUgM0QgcGxvdHMgd2l0aCB5b3VyIG1vdXNlKTo8YnIvPgo8YSBocmVmPSIjdm9sQ2F0ZWdvcnlQbG90cyI+Vm9sdW1lIENhdGVnb3J5IEhpc3RvZ3JhbXM8L2E+IHwKPGEgaHJlZj0iI2NvZmZlZXJpbmciPldoZXJlIG11b25zIGRpZSAoY29mZmVlIHJpbmcgcGxvdHMpPC9hPiB8CjxhIGhyZWY9IiNtdW9uaW5pcm9uIj5NdW9uIHJhbmdpbmcgb3V0IGluIGlyb24geW9rZTwvYT4gfAo8YSBocmVmPSIjbXVvblNjYXRJbmZsIj5NdW9uIHNjYXR0ZXJzIGluIGluZmxlY3RvcjwvYT4gfAo8YSBocmVmPSIjaGlzdGRpc3RyaW5nIj5EaXN0YW5jZSBkaXN0cmlidXRpb24gaW4gUmluZyBpcm9uPC9hPiB8CjxhIGhyZWY9IiNlbmVyZ3lsb3NzIj5FbmVyZ3kgbG9zcyBpbiB2b2x1bWVzPC9hPiB8CjxhIGhyZWY9IiNuc3RlcHMiPk51bWJlciBvZiBzdGVwcyBpbiB2b2x1bWVzPC9hPiB8CjxhIGhyZWY9IiNtdW9uSXJvbkxvbmciPkFub3RoZXIgbXVvbiBpbiB0aGUgaXJvbjwvYT4gfAo8YSBocmVmPSIjbG9uZ011b24iPlN0b3JlZCBtdW9uPC9hPiB8CjxhIGhyZWY9IiNjYm8iPkNvaGVyZW50IEJldGF0cm9uIE9zY2lsbGF0aW9uPC9hPgoKIyBJbnRyb2R1Y3Rpb24gCgpUaGUgZ29hbCBvZiB0aGlzIGRvY3VtZW50IGlzIHRvIGV4YW1pbmUgImVzY2FwZWQgbXVvbnMiIHRoYXQgZG8gbm90IGRlY2F5IGluIHRoZSBzdG9yYWdlIHJlZ2lvbiBhbmQgZG8gbm90IGVudGVyIGEgY2Fsb3JpbWV0ZXIuIFNvbWUgb2YgdGhlc2UgbXVvbnMgbWF5IGxlYXZlIHRoZSBzdG9yYWdlIHJpbmcgYWx0b2dldGhlci4gCgpJIGdlbmVyYXRlZCBhcnQgZmlsZXMgb2Ygc2ltdWxhdGVkIGV2ZW50cyB1c2luZyBgbWRjMmAgd2l0aCBkaWZmZXJlbnQgbWFnbmV0aWMgZmllbGRzIHR1cm5lZCBvbiBvciBvZmYgKHNjZW5hcmlvcykuICoqTm90ZToqKiBUaGVzZSBmaWxlcyBhcmUgc3RpbGwgbm90IHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBNREMyLiBOYXRoYW4gaXMgdHVuaW5nIHVwIHRoZSBpbmplY3Rpb24gZ3VuLiBTbyByaWdodCBub3cgdGhlIHN0b3JhZ2UgZWZmaWNpZW5jeSBJIHNlZSAoMC41JSkgaXMgc3RpbGwgbG93LiBTaG91bGQgZ2V0IGJldHRlciB3aXRoIHRoZSBuZXh0IHJvdW5kLgpXZSB3YW50IHRvIGV4YW1pbmUsCgoqIE11b25zIHRoYXQgYXJlIGVhdGVuIGJ5IHRoZSBpcm9uIHlva2UgYW5kIG11b25zIHRoYXQgZXNjYXBlIHRoZSB3b3JsZCB2b2x1bWUgZW50aXJlbHkuIAoqIFdoZXJlIGRvIG11b25zIGVzY2FwZSB0aGUgd29ybGQgKDNEIGFuZCBoZWF0IG1hcCBwbG90cz8pCiogQ29tcGFyaXNvbiBvZiB0aGUgZGlmZmVyZW50IHNjZW5hcmlvcwoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkocmV0aWN1bGF0ZSkgICMgQWNjZXNzIHRvIHB5dGhvbgpsaWJyYXJ5KHN0cmluZ3IpICAgIyBzdHJpbmcgZnVuY3Rpb25zCmxpYnJhcnkocGFyYWxsZWwpICAjIFBhcmFsbGVsIHByb2Nlc3NpbmcgKGJ1aWx0LWluIHRvIFIpCmxpYnJhcnkoZHBseXIpICAgICAjIGRhdGEgYW5hbHlzaXMKbGlicmFyeSh0aWR5cikgICAgICMgUmVzaGFwZSAKbGlicmFyeShwdXJycikgICAgICMgRnVuY3Rpb25hbCBwcm9ncmFtbWluZwpsaWJyYXJ5KHJlYWRyKSAgICAgIyBSZWFkIGZvcm1hdHMKbGlicmFyeShnZ3Bsb3QyKSAgICMgcGxvdGluZwpsaWJyYXJ5KHJnbCkgICAgICAgIyAzRCBwbG90cwpsaWJyYXJ5KGdyaWRFeHRyYSkgIyBBcnJhbmdpbmcgcGxvdHMKbGlicmFyeSh0ZXN0dGhhdCkgICMgdGVzdGluZwojCmxpYnJhcnkocmVhZEdhbGxlcnkpICMgUmVhZCBhcnQgR2FsbGVyeSBmaWxlcwpgYGAKCmBgYHtyfQojIFB1dCBhbGwgcmVhZEdhbGxlcnk6OnVzZURhdGFQcm9kdWN0IGNhbGxzIGhlcmUKdXNlRGF0YVByb2R1Y3QoJ3N0ZDo6dmVjdG9yPGdtMnRydXRoOjpUcmFja2luZ0FjdGlvbkFydFJlY29yZD4nKQp1c2VEYXRhUHJvZHVjdCgnc3RkOjp2ZWN0b3I8Z20ycmluZ3NpbTo6R2VhbnRUcmFja1JlY29yZD4nKQp1c2VEYXRhUHJvZHVjdCgnc3RkOjp2ZWN0b3I8Z20ydHJ1dGg6Okdob3N0RGV0ZWN0b3JBcnRSZWNvcmQ+JykKYGBgCgpgYGB7cn0KIyBUbyByZXN0b3JlIG1vc3Qgb2YgdGhlIGVudmlyb25tZW50CnRyYWNraW5nQWN0aW9uREYgPC0gcmVhZF9yZHMoJ3RyYWNraW5nQWN0aW9uREYucmRzJykKdHJhY2tpbmdBY3Rpb25OREYgPC0gcmVhZF9yZHMoJ3RyYWNraW5nQWN0aW9uTkRGLnJkcycpCnZvbE5hbWVERiA8LSByZWFkX3Jkcygndm9sTmFtZURGLnJkcycpCmdoQ3lsREYgPC0gcmVhZF9yZHMoJ2doQ3lsREYucmRzJykKZ2hXbGRERiA8LSByZWFkX3JkcygnZ2hXbGRERi5yZHMnKQpgYGAKCiMgTG9hZCB0aGUgc2FtcGxlcwoKU2FtcGxlcyBhcmUgbG9jYXRlZCBvbiB0aGUgRmVybWlsYWIgZENhY2hlIGluIHRoZSBkaXJlY3RvcnkgYC9wbmZzL0dNMi9zY3JhdGNoL3VzZXJzL2x5b24vYXJyXzIwMTcwMzIxYC4gCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBwcm9wZXJseSBhbHRlciBwYXRocyB0byBhY2NvbW9kYXRlIFhSb290RAojICAgIGUuZy4gY29udmVydCAvcG5mcy9CTEEgLS0+IHJvb3Q6Ly9mbmRjYTEuZm5hbC5nb3YvcG5mcy9mbmFsLmdvdi91c3IvQkxBCnhyb290ZF9pZnkgPC0gZnVuY3Rpb24oYVBhdGgpIHBhc3RlMCgncm9vdDovL2ZuZGNhMS5mbmFsLmdvdi9wbmZzL2ZuYWwuZ292L3VzcicsIHN0cl9yZXBsYWNlKGFQYXRoLCAnL3BuZnMnLCAnJykpCmBgYAoKRGV0ZXJtaW5lIHRoZSBsb2NhdGlvbnMgb2YgdGhlIGRhdGEgZmlsZXMKYGBge3J9CiMgRGV0ZXJtaW5lIGxvY2F0aW9ucyBvZiBvdXIgZmlsZXMKc3lzdGVtKCJpZmRoIGxzICcvcG5mcy9HTTIvc2NyYXRjaC91c2Vycy9seW9uL2Fycl8yMDE3MDMyMS8qLyoucm9vdCcgfCBncmVwIEFSUiIsIGludGVybj1UKSAlPiUgc29ydCgpIC0+IGFyckZpbGVzCmBgYApgYGB7cn0KYXJyRmlsZXMgJT4lIHhyb290aWZ5KCkgLT4gYXJyWHJkRmlsZXMKYXJyWHJkRmlsZXMKYGBgCmBgYHtyfQp3cml0ZV9yZHMoYXJyRmlsZXMsICdhcnJGaWxlcy5yZHMnKQpgYGAKClB1bGwgb3V0IHRoZSBzY2VuYWlvcyAodGhlc2Ugd2lsbCBiZSBpbiB0aGUgc2FtZSBvcmRlciBhcyB0aGUgZmlsZSBuYW1lcykKYGBge3J9CnNjZW5hcmlvcyA8LSBzdHJfbWF0Y2goYXJyRmlsZXMsICdhcnJfMjAxNzAzMjEvLit1bmlmaWVkXyguKylfY3lsJylbLDJdCnNjZW5hcmlvcyAlPiUgdW5pcXVlKCkKYGBgCgpMZXQncyBsb29rIGF0IHRoZSBjb250ZW50cyBvZiB0aGVzZSBmaWxlcyAodGhleSdyZSBhbGwgdGhlIHNhbWUsIHNvIHdlJ2xsIGp1c3QgZG8gb25lKSBgZ20yIHY3XzA0XzAwYCB3aWxsIGhhdmUgYHByb2R1Y3Rfc2l6ZXNfZHVtcGVyYC4gCmBgYHtyfQpzdHJpbmdUb1J1biA8LSBzdHJfaW50ZXJwKAogICJzc2ggbHlvbkBnbTJncHZtMDQuZm5hbC5nb3YgJ3NvdXJjZSAvY3ZtZnMvZ20yLm9wZW5zY2llbmNlZ3JpZC5vcmcvcHJvZDcvZy0yL3NldHVwCiAgIHNldHVwIGdtMiB2N18wNF8wMCAtcSBwcm9mIDsgCiAgIHByb2R1Y3Rfc2l6ZXNfZHVtcGVyICR7YUZpbGV9IHwgZ3JlcCA6OiciLCAKICBsaXN0KGFGaWxlID0gYXJyRmlsZXNbMV0gJT4lIHN0cl9yZXBsYWNlKCdmbmFsLmdvdi91c3IvJywnJykpCikKIwpzeXN0ZW0oc3RyaW5nVG9SdW4pCmBgYAojIEVzY2FwaW5nIG11b25zIGZyb20gdGhlIFRyYWNraW5nIEFjdGlvbiBkYXRhCgpUaGUgdHJhY2tpbmcgYWN0aW9uIGRhdGEgaGFzIGJpcnRoIGFuZCBkZWF0aCBkYXRhIGZvciBldmVyeSBHZWFudCB0cmFjay4gCgojIyBMb2FkIFRyYWNraW5nIEFjdGlvbiBkYXRhIAoKV2UnbGwgY2FwdHVyZSB0aGlzIGluZm9ybWF0aW9uIGZvciB0aGUgcHJpbWFyeSBtdW9uIGFuZCBpdHMgZmlyc3QgZ2VuZXJhdGlvbiBkYXVnaHRlcnMuIFNlZSBbZ20yZGF0YXByb2R1Y3RzL21jL2FjdGlvbnMvdHJhY2svVHJhY2tpbmdBY3Rpb25BcnRSZWNvcmQuaGhdKGh0dHBzOi8vcmVkbWluZS5mbmFsLmdvdi9yZWRtaW5lL3Byb2plY3RzL2dtMmRhdGFwcm9kdWN0cy9yZXBvc2l0b3J5L2VudHJ5L21jL2FjdGlvbnMvdHJhY2svVHJhY2tpbmdBY3Rpb25BcnRSZWNvcmQuaGg/cmV2PWZlYXR1cmUlMkZtZGMyKS4gCgpXZSBuZWVkIGEgYHJlYWRHYWxsZXJ5YCByZWFkZXIgcHl0aG9uIGNsYXNzLiBHZW5lcmF0ZSB3aXRoLApgYGByCnJlYWRlckNsYXNzU2tlbCgnZ20ydHJ1dGg6OlRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkJywgd3JpdGVGaWxlID0gJ3RyYWNraW5nQWN0aW9uUmVhZGVyLnB5JykKYGBgCgpIZXJlIGlzIHRoZSByZWFkZXIsCmBgYHtyfQpyZWFkX2ZpbGUoJ3RyYWNraW5nQWN0aW9uUmVhZGVyLnB5JykgJT4lIGNhdApgYGAKCk1ha2UgdGhlIHJlYWRlciBjbGFzcyBhbmQgb2JqZWN0CmBgYHtyfQp0cmFja2luZ0FjdGlvblJlYWRlckMgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCd0cmFja2luZ0FjdGlvblJlYWRlci5weScpJFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkUmVhZGVyCmBgYApXZSBoYXZlIG1hbnkgZmlsZXMgdG8gcHJvY2Vzcy4gTGV0J3MgZG8gdGhpcyByZWFkaW5nIGluIGBwYXJhbGxlbGAuIAoKV2UgbmVlZCB0byBjcmVhdGUgYSByZWFkZXIgb2JqZWN0IHBlciBwYXJhbGxlbCBqb2IKYGBge3J9CnRyYWNraW5nQWN0aW9uUmVhZGVycyA8LSBtYXAoMTpsZW5ndGgoYXJyWHJkRmlsZXMpLCBmdW5jdGlvbihpKSB0cmFja2luZ0FjdGlvblJlYWRlckMoYXJ0SW5wdXRUYWcoImFydGc0IikpKQpgYGAKCkxvYWQgdGhlIGRhdGEKYGBge3J9CiMgRm9yIHNvbWUgcmVhc29uIHRoZXNlIGpvYnMgc2VlbSB0byBydW4gc2VyaWFsbHkKam9icyA8LSBsYXBwbHkoMTpsZW5ndGgoYXJyWHJkRmlsZXMpLCBmdW5jdGlvbihpKSBnZXRHYWxsZXJ5RGF0YShhcnJYcmRGaWxlc1tpXSwgdHJhY2tpbmdBY3Rpb25SZWFkZXJzW1tpXV0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWNwYXJhbGxlbChuYW1lPWkpKQp0cmFja2luZ0FjdGlvblJldHVybnMgPC0gbWNjb2xsZWN0KGpvYnMpCmBgYAoKTGV0J3MgbWVyZ2UgYWxsIG9mIHRoZSBvdXRwdXQuCgpgYGB7cn0KdHJhY2tpbmdBY3Rpb25ERiA8LSBtYXBfZGYoMTpsZW5ndGgodHJhY2tpbmdBY3Rpb25SZWFkZXJzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGkpIGdhbGxlcnlSZWFkZXJfZGYodHJhY2tpbmdBY3Rpb25SZWFkZXJzW1tpXV0pICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoc2NlbmFyaW89c2NlbmFyaW9zW2ldLGZpbGVFbnRyeT1pKSkKYGBgCmBgYHtyfQpucm93KHRyYWNraW5nQWN0aW9uREYpCnRyYWNraW5nQWN0aW9uREYgJT4lIGhlYWQoMTAwKSAgIyBMZXQncyBub3QgZHVtcCB0aGUgZW50aXJlIGRhdGEgZnJhbWUgaW50byB0aGUgSFRNTCBwYWdlCmBgYApgYGB7cn0KIyBXcml0ZSB0aGlzIG91dCBzbyB3ZSBkb24ndCBuZWVkIHRvIGRvIHRoZSBhYm92ZSBhZ2Fpbgp3cml0ZV9yZHModHJhY2tpbmdBY3Rpb25ERiwgJ3RyYWNraW5nQWN0aW9uREYucmRzJykKYGBgCgpIb3cgbWFueSBtdW9ucyBwZXIgZmlsZT8KYGBge3J9CnRyYWNraW5nQWN0aW9uREYgJT4lIGZpbHRlcih0cmFja0lEPT0xKSAlPiUgZ3JvdXBfYnkoc2NlbmFyaW8sIGZpbGVFbnRyeSkgJT4lIHRhbGx5KCkKYGBgCgojIyMgQWRkIFZvbHVtZSBOYW1lcwoKV2UgbmVlZCB0byB0dXJuIHRoZSB2b2x1bWUgSURzIGludG8gdm9sdW1lIG5hbWVzLiBUaGUgdm9sdW1lIElEcyBjaGFuZ2UgZm9yIGVhY2ggZmlsZS4gV2UgY2FuIHJ1biBhIHNtYWxsIGFydCBqb2IgdG8gZGV0ZXJtaW5lIHRoZSB2b2x1bWUgSUQgdG8gbmFtZSB0YWJsZXMuIAoKYGBge3J9CnJ1bkZvclZvbElEU3RyaW5nIDwtIGZ1bmN0aW9uKGkpIHsKICBzdHJfaW50ZXJwKAogICAgIlBWU19DU1ZPVVQ9JHtjc3ZvdXR9XyR7aX1fdm9sTmFtZXMuY3N2IGdtMiAtYyAke2ZjbFBhdGh9L2dtMmFuYWx5c2VzL2ZjbC9waHlzaWNhbFZvbHVtZVN0b3JlVG9GaWxlLmZjbCAtbiAxICR7YUZpbGV9IiwKICAgIGxpc3QoIGNzdm91dD1zY2VuYXJpb3NbaV0sIGk9aSwgZmNsUGF0aD1TeXMuZ2V0ZW52KCJNUkJfQlVJTERESVIiKSwgYUZpbGU9YXJyWHJkRmlsZXNbaV0pIAogICkKfQpydW5Gb3JWb2xJRFN0cmluZ3MgPC0gc2FwcGx5KDE6bGVuZ3RoKGFyclhyZEZpbGVzKSwgcnVuRm9yVm9sSURTdHJpbmcpCnJ1bkZvclZvbElEU3RyaW5ncwpgYGAKTGV0J3MgcnVuIHRoZSBhcnQgam9icyBpbiBwYXJhbGxlbAoKYGBge3J9CmpvYnMgPC0gbGFwcGx5KDE6bGVuZ3RoKGFyclhyZEZpbGVzKSwgZnVuY3Rpb24oaSkgbWNwYXJhbGxlbChzeXN0ZW0oIHJ1bkZvclZvbElEU3RyaW5nc1tpXSwgaW50ZXJuPVQgKSwgbmFtZT1pKSkKcmVzIDwtIG1jY29sbGVjdChqb2JzKQpgYGAKCmFuZCBsb2FkIHRoZSBDU1YgZmlsZXMuIApgYGB7ciBtZXNzYWdlPUZBTFNFfQpqb2JzIDwtIGxhcHBseSgxOmxlbmd0aChhcnJGaWxlcyksIGZ1bmN0aW9uKGkpIAogIHN0cl9pbnRlcnAoIiR7c2NufV8ke2l9X3ZvbE5hbWVzLmNzdiIsIGxpc3Qoc2NuPXNjZW5hcmlvc1tpXSwgaT1pKSkgJT4lCiAgICByZWFkX2Nzdihjb2xfbmFtZXM9Yygidm9sdW1lVUlEIiwgInZvbE5hbWUiKSkgJT4lIG1jcGFyYWxsZWwobmFtZT1pKSApCiMKdm9sTmFtZVRhYmxlcyA8LSBtY2NvbGxlY3Qoam9icykKdm9sTmFtZURGIDwtIG1hcF9kZigxOmxlbmd0aCh2b2xOYW1lVGFibGVzKSwgZnVuY3Rpb24oaSkgCiAgICAgICAgICAgICAgICAgICAgICB2b2xOYW1lVGFibGVzW1tpXV0gJT4lIG11dGF0ZShzY2VuYXJpbz1zY2VuYXJpb3NbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlRW50cnk9aSkpCm5yb3codm9sTmFtZURGKQp2b2xOYW1lREYgJT4lIGhlYWQoMTAwKQpgYGAKYGBge3J9CndyaXRlX3Jkcyh2b2xOYW1lREYsICd2b2xOYW1lREYucmRzJykKYGBgCgpOb3cgd2UgbWVyZ2Ugd2l0aCB0aGUgdHJhY2tpbmcgYWN0aW9uIGRhdGEgZnJhbWUKYGBge3J9CnRyYWNraW5nQWN0aW9uREYgJT4lIGlubmVyX2pvaW4odm9sTmFtZURGKSAlPiUgCiAgc2VsZWN0KHNjZW5hcmlvLCBmaWxlRW50cnksIGV2ZW50RW50cnksIHZvbE5hbWUsIGV2ZXJ5dGhpbmcoKSkgLT4gdHJhY2tpbmdBY3Rpb25OREYKbnJvdyh0cmFja2luZ0FjdGlvbk5ERikKdHJhY2tpbmdBY3Rpb25OREYgJT4lIGhlYWQoMTAwKQpgYGAKCiMjIEhvdyBtYW55IG11b25zIGRlY2F5IGluIHRoZSBzdG9yYWdlIHJlZ2lvbj8KClRoZXNlIG11b25zIGRlY2F5IGluIHRoZSBzdG9yYWdlIHJlZ2lvbiAobm90IHF1aXRlIHRoZSBzYW1lIGFzICJzdG9yZWQiKSwKYGBge3J9CnRyYWNraW5nQWN0aW9uTkRGICU+JSBmaWx0ZXIodHJhY2tJRD09MSwgdm9sTmFtZT09J0FyY1NlY3Rpb25bMDBdJykgJT4lIAogIGdyb3VwX2J5KHNjZW5hcmlvLCBmaWxlRW50cnkpICU+JSB0YWxseSgpCmBgYApJbnRlcmVzdGluZyEgU28sIGZyb20gdGhpcywgb25seSBgciAxMTUvMjAwMDAqMTAwYCUgZGVjYXkgaW4gdGhlIHN0b3JhZ2UgcmVnaW9uIGZvciB0aGUgImFsbCBvbiIgKG9yIGV2ZXJ5dGhpbmcpIHNhbXBsZS4gVGhhdCBzZWVtcyByYXRoZXIgbG93LiBCdXQgY29udGludWluZy4gCmBgYHtyfQp3cml0ZV9yZHModHJhY2tpbmdBY3Rpb25OREYsICd0cmFja2luZ0FjdGlvbk5ERi5yZHMnKQpgYGAKCiMjIFdoZXJlIGRvIG11b25zIGdvIHRvIGRpZT8KCmBgYHtyfQp0cmFja2luZ0FjdGlvbk5ERiAlPiUgZmlsdGVyKHRyYWNrSUQgPT0gMSkgJT4lIGdyb3VwX2J5KHNjZW5hcmlvLCB2b2xOYW1lKSAlPiUgdGFsbHkoKQpgYGAKSXQgbWF5IGJlIG5pY2UgdG8gY2F0ZWdvcml6ZSB0aGUgdm9sdW1lIG5hbWVzIGJ5IGFyZWEgKGUuZy4gIkFyYyIsICJiZWxsb3dzIiwgIlJpbmciLCAiSW5mbGVjdG9yIikuCgpgYGB7cn0KIyBQdWxsIG91dCB0aGUgZmlyc3Qgd29yZCBpbiBjYW1lbCBjYXNlIChzZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yOTkxNjA2NS9ob3ctdG8tZG8tY2FtZWxjYXNlLXNwbGl0LWluLXB5dGhvbikKY2FtZWxDYXNlU3BsaXQgPC0gZnVuY3Rpb24ocykgc3RyX3NwbGl0KHMsICcoPzw9W2Etel0pKD89W0EtWl0pfCg/PD1bQS1aXSkoPz1bQS1aXVthLXpdKScpCmBgYAoKYGBge3J9CiMgVGVzdCB0aGUgYWJvdmUKZXhwZWN0X2VxdWFsKGNhbWVsQ2FzZVNwbGl0KCJSaW5nUG9sZVRpcFVwcGVyIilbWzFdXSwgYygiUmluZyIsICJQb2xlIiwgIlRpcCIsICJVcHBlciIpKQpleHBlY3RfZXF1YWwoY2FtZWxDYXNlU3BsaXQoInJpbmdQb2xlVGlwVXBwZXIiKVtbMV1dLCBjKCJyaW5nIiwgIlBvbGUiLCAiVGlwIiwgIlVwcGVyIikpCmV4cGVjdF9lcXVhbChjYW1lbENhc2VTcGxpdCgid29ybGQiKVtbMV1dLCBjKCJ3b3JsZCIpKQpleHBlY3RfZXF1YWwoY2FtZWxDYXNlU3BsaXQoInJpbmdQb2xlIilbWzFdXSwgYygicmluZyIsICJQb2xlIikpCmBgYAoKYGBge3J9CiMgV2UgcmVhbGx5IGp1c3Qgd2FudCB0aGUgZmlyc3Qgd29yZAp0YWtlRmlyc3RDYW1lbCA8LSBmdW5jdGlvbihzKSBjYW1lbENhc2VTcGxpdChzKSAlPiUgbWFwX2NocihmdW5jdGlvbihzKSBzWzFdKQojCiMgVGVzdAp0YWtlRmlyc3RDYW1lbCgiUmluZ1BvbGVUaXBVcHBlciIpICU+JSBleHBlY3RfZXF1YWwoIlJpbmciKQp0YWtlRmlyc3RDYW1lbCgicmluZ1BvbGVUaXBVcHBlciIpICU+JSBleHBlY3RfZXF1YWwoInJpbmciKQp0YWtlRmlyc3RDYW1lbCgid29ybGQiKSAlPiUgZXhwZWN0X2VxdWFsKCJ3b3JsZCIpCnRha2VGaXJzdENhbWVsKCJyaW5nUG9sZSIpICU+JSBleHBlY3RfZXF1YWwoInJpbmciKQp0YWtlRmlyc3RDYW1lbChjKCJyaW5nUG9sZVRpcFVwcGVyIiwgIndvcmxkIikpICU+JSBleHBlY3RfZXF1YWwoYygicmluZyIsICJ3b3JsZCIpKQpgYGAKCkRvbid0IGNvbmZ1c2UgYFJpbmdUcmFja2luZ1BsYW5lYCB3aXRoIHRoZSBpcm9uIChhbHNvIHN0YXJ0cyB3aXRoIFJpbmcpCmBgYHtyfQptYWtlVm9sQ2F0ZWdvcnkgPC0gZnVuY3Rpb24ocykgaWZlbHNlKCBzdHJfZGV0ZWN0KHMsICdSaW5nVHJhY2tpbmcnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJuZ1Rya1BsYW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFrZUZpcnN0Q2FtZWwocykpCmBgYApgYGB7cn0KbWFrZVZvbENhdGVnb3J5KGMoIlJpbmdCb3R0b20iLCAiUmluZ1RyYWNraW5nUGxhbmVbOF0iLCAid29ybGQiKSkgJT4lIGV4cGVjdF9lcXVhbChjKCJSaW5nIiwgIlJuZ1Rya1BsYW5lIiwgIndvcmxkIikpCmBgYAoKCmBgYHtyfQp0cmFja2luZ0FjdGlvbk5ERiAlPiUgbXV0YXRlKHZvbENhdGVnb3J5ID0gdm9sTmFtZSAlPiUgbWFrZVZvbENhdGVnb3J5KCkpIC0+IHRyYWNraW5nQWN0aW9uTlZERgp0cmFja2luZ0FjdGlvbk5WREYgJT4lIGhlYWQoMTAwKQpgYGAKU2hvdyBjb3VudCBieSBjYXRlZ29yeQpgYGB7cn0KdHJhY2tpbmdBY3Rpb25OVkRGICU+JSBmaWx0ZXIodHJhY2tJRCA9PSAxKSAlPiUgZ3JvdXBfYnkoc2NlbmFyaW8sIHZvbENhdGVnb3J5KSAlPiUgdGFsbHkoKSAtPiB2b2xDYXRUYWxseQp2b2xDYXRUYWxseSAlPiUgbXV0YXRlKHBlcmMgPSBuLzIwMDAwICogMTAwKSAtPiB2b2xDYXRUYWxseQp2b2xDYXRUYWxseQpgYGAKPGEgbmFtZT0idm9sQ2F0ZWdvcnlQbG90cyIvPkxldCdzIHBsb3QhCmBgYHtyfQp0cmFja2luZ0FjdGlvbk5WREYgJT4lIGZpbHRlcih0cmFja0lEID09MSkgJT4lIGdncGxvdChhZXMoeD12b2xDYXRlZ29yeSwgZ3JvdXA9c2NlbmFyaW8pKSArIAogIGdlb21fYmFyKCkKYGBgCmBgYHtyfQpnZ3Bsb3Qodm9sQ2F0VGFsbHksIGFlcyh4ID0gdm9sQ2F0ZWdvcnksIHk9cGVyYywgZmlsbD1zY2VuYXJpbykpICsgCiAgZ2VvbV9iYXIoc3RhdD0iSWRlbnRpdHkiLCB3aWR0aD0wLjUsIHBvc2l0aW9uPSJkb2RnZSIpCmBgYApDYW4ndCByZWFsbHkgc2VlIG5vbi1SaW5nL3dvcmxkIG9uZXMuICBMZXQncyBzcGxpdCB0aGVtIG91dC4KCmBgYHtyfQojIEZ1bmN0aW9uIHRvIG1ha2UgdGhpcyBlYXN5CnBsb3RpdCA8LSBmdW5jdGlvbihkKSBnZ3Bsb3QoZCwgYWVzKHggPSB2b2xDYXRlZ29yeSwgeT1wZXJjLCBmaWxsPXNjZW5hcmlvKSApICsgCiAgZ2VvbV9iYXIoc3RhdD0iSWRlbnRpdHkiLCB3aWR0aD0wLjUsIHBvc2l0aW9uPSJkb2RnZSIpICsgCiAgeGxhYigiVm9sdW1lIGNhdGVnb3J5IikgKyB5bGFiKCJQZXJjZW50IikKCnZvbENhdFRhbGx5ICU+JSBmaWx0ZXIodm9sQ2F0ZWdvcnkgJWluJSBjKCJSaW5nIiwgIndvcmxkIikpICU+JSBwbG90aXQgLT4gcDEKCnZvbENhdFRhbGx5ICU+JSBmaWx0ZXIoISB2b2xDYXRlZ29yeSAlaW4lIGMoIlJpbmciLCAid29ybGQiKSkgJT4lIHBsb3RpdCAtPiBwMgoKZ3JpZC5hcnJhbmdlKHAxLCBwMikKYGBgCkxldCdzIHNlZSB3aGVyZSB0aGV5IGRpZQpgYGB7cn0KIyBTZWUgaHR0cHM6Ly9ycHVicy5jb20vaGFkbGV5Lzk3OTcwIGZvciBob3cgdG8gd3JhcCBhIG11bHRpcGFydCBnZ3Bsb3QyIGNvbXBvbmVudApwbG90Q29tbW9uIDwtIGZ1bmN0aW9uICgpIHsKICBsaXN0KAogICAgZmFjZXRfd3JhcCh+c2NlbmFyaW8pLAogICAgZ3VpZGVzKGNvbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NSwgYWxwaGE9MSkpKSwKICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWU9IlZvbHVtZVxuQ2F0ZWdvcnkiKSwKICAgIGxhYnMoeD0ieiAobW0pIiwgeT0ieCAobW0pIiksCiAgICB0aGVtZV9taW5pbWFsKCkKICApCn0KYGBgCgo8YSBuYW1lPSJjb2ZmZWVyaW5nIi8+VGhlICJDb2ZmZWUgUmluZyIgcGxvdHMKYGBge3IgZmlnLmhlaWdodD04fQp0cmFja2luZ0FjdGlvbk5WREYgJT4lIGZpbHRlcih0cmFja0lEID09IDEpICU+JSAKICBnZ3Bsb3QoIGFlcyh4PXosIHk9eCwgY29sb3I9dm9sQ2F0ZWdvcnkpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYT0wLjEpICsgCiAgICBnZ3RpdGxlKCdXaGVyZSBNdW9ucyBEaWUnKSArCiAgICBwbG90Q29tbW9uKCkKYGBgCkhlcmUgaXQgaXMgd2l0aG91dCB0aGUgcmluZyBsb3NzZXMsIHdoaWNoIGRvbWluYXRlCmBgYHtyIGZpZy5oZWlnaHQ9OH0KdHJhY2tpbmdBY3Rpb25OVkRGICU+JSBmaWx0ZXIodHJhY2tJRCA9PSAxLCB2b2xDYXRlZ29yeSAhPSAnUmluZycpICU+JSAKICBnZ3Bsb3QoIGFlcyh4PXosIHk9eCwgY29sb3I9dm9sQ2F0ZWdvcnkpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsgCiAgICBnZ3RpdGxlKCJXaGVyZSBNdW9ucyBEaWUiLCBzdWJ0aXRsZSA9ICJSaW5nIGVzY2FwZWVzIGV4Y2x1ZGVkIGZvciBjbGFyaXR5IikgKwogICAgcGxvdENvbW1vbigpCmBgYAoKIyMgRG9lcyBpdCBtYWtlIHNlbnNlIGZvciBzbyBtYW55IG11b25zIHRvIHN0b3AgaW4gdGhlIGlyb24/CgpXZSBoYXZlIDMgR2VWIG11b25zLiBEb2VzIGl0IG1ha2Ugc2Vuc2UgdGhhdCB0aGUgdmFzdCBtYWpvcml0eSBvZiB0aGVtIHN0b3AgaW4gdGhlIGlyb24/IEEgW3RhYmxlXShodHRwOi8vcGRnLmxibC5nb3YvMjAxNi9BdG9taWNOdWNsZWFyUHJvcGVydGllcy9NVUUvbXVFX2lyb25fRmUucGRmKSBzaG93cyB0aGUgQ29udGludW91cyBTbG93IERvd24gQXBwcm94aW1hdGlvbiByYW5nZSBvZiBtdW9ucyAoQ1NEQSkuIEZvciBhIDMgR2VWIG11b24gaW4gaXJvbiwgdGhlIENTREEgaXMgJDEuODI1IFx0aW1lcyAxMF57M30kIGcvY20kXjIkLiBUaGUgZGVuc2l0eSAkXHJobyQgaXMgNy44NzQgZy9jbSReMyQuIFNvLCB3aGVyZSAkUiQgaXMgcmFuZ2UsIAoKJCRSID0gXHRleHR7Q1NEQX0vXHJobyQkClJhbmdlIGlzIHRodXMgYHIgcm91bmQoMTgyNS83Ljg3NC8xMDAsIDIpYCBtLiBUaGlzIGlzIGJpZ2dlciB0aGFuIHRoZSB3aWR0aCBvZiB0aGUgaXJvbiwgYnV0IHJlbWVtYmVyIHRoYXQgdGhlIG11b25zIGNvbWUgaW4gYXQgYW4gb2JsaXF1ZSBhbmdsZS4gV2UnbGwgaGF2ZSB0byBwcm92ZSB0aGlzLiAKCiMgSG93IGZhciBkbyBtdW9ucyBnbyBpbiB0aGUgaXJvbj8KCldlIGhhdmUgdGhlIEdlYW50IHN0ZXBwaW5nIGluZm9ybWF0aW9uLCBzbyB3ZSBzaG91bGQgYmUgYWJsZSB0byBmaWd1cmUgb3V0IGhvdyBmYXIgdGhlIG11b25zIGdvIGluIHRoZSBpcm9uLiAKCkhlcmUgaXMgdGhlIEdhbGxlcnkgcmVhZGVyLApgYGB7cn0KcmVhZF9maWxlKCdHZWFudFRyYWNrUmVhZGVyLnB5JykgJT4lIGNhdApgYGAKYGBge3J9CmdlYW50U3RlcFJlYWRlckMgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCdHZWFudFRyYWNrUmVhZGVyLnB5JykkR2VhbnRTdGVwUmVhZGVyCmBgYAoKIyMgTXVvbiBkaXN0YW5jZSBpbiBpcm9uIGZvciBvbmUgZXZlbnQKCkxldCdzIGZpbmQgYW4gZXZlbnQgd2hlcmUgdGhlIG11b24gZGlkIGluIHRoZSBpcm9uLiAKCmBgYHtyfQp0cmFja2luZ0FjdGlvbk5WREYgJT4lIGZpbHRlcih0cmFja0lEID09IDEsIHZvbE5hbWUgPT0gJ1JpbmdZb2tlQm90dG9tJykgJT4lIGhlYWQoMTAwKQpgYGAKRXZlbnQgNCBsb29rcyBnb29kLiAKCmBgYHtyfQpnZWFudFN0ZXBPbmUgPC0gZ2VhbnRTdGVwUmVhZGVyQyhhcnRJbnB1dFRhZygnYXJ0ZzQ6S2VlcFN0ZXBzQWN0aW9uJyksIHR1cGxlKGFzLmludGVnZXIoNCkpKQpgYGAKYGBge3J9CmdldEdhbGxlcnlEYXRhKGFyclhyZEZpbGVzWzFdLCBnZWFudFN0ZXBPbmUpCmBgYApgYGB7cn0KZ3NPbmVERiA8LSBnYWxsZXJ5UmVhZGVyX2RmKGdlYW50U3RlcE9uZSkKYGBgCgpNZXJnZSBpbiB2b2x1bWUgbmFtZXMKYGBge3J9CmdzT25lREYgJT4lIG11dGF0ZShmaWxlRW50cnkgPSAxKSAlPiUKICBpbm5lcl9qb2luKHZvbE5hbWVERikgJT4lIAogIHNlbGVjdCh2b2xOYW1lLCBldmVyeXRoaW5nKCkpIC0+IGdzT25lREYKZ3NPbmVERgpgYGAKTGV0J3MgbWFrZSBhIDNEIHBsb3Qgb2YgdGhpcyBwYXRoCgpgYGB7cn0KcmluZyA8LSBjeWxpbmRlcjNkKCBjZW50ZXI9cmJpbmQoYygwLC05MCwgMCksIGMoMCw5MCwwKSksCiAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTcxMTIsCiAgICAgICAgICAgICAgICAgICAgc2lkZXM9MjAsIGNsb3NlZCA9IEYpCmBgYAoKTGV0J3MgZG8gdm9sdW1lIGNhdGVnb3JpZXMgYWdhaW4uCmBgYHtyfQpnc09uZURGICU+JSBtdXRhdGUodm9sQ2F0ZWdvcnkgPSB2b2xOYW1lICU+JSBtYWtlVm9sQ2F0ZWdvcnkgJT4lIGFzLmZhY3RvcikgLT4gZ3NPbmVERgpgYGAKCjxhIG5hbWU9Im11b25pbmlyb24iLz5MZXQncyBwbG90IHRoZSBwYXRoLiBOb3RlIHRoYXQgSSBoYXZlIHRvIG1ha2UgdGhlIGxlZ2VuZCBzZXBhcmF0ZWx5IHNpbmNlIGBsZWdlbmQzZGAgbG9va3MgYXdmdWwuIApgYGB7ciBmaWcud2lkdGg9NX0KIyBsZW5nZW5kM2QgbG9va3MgdGVycmlibGUgLSBzbyBkbyBhIHJlZ3VsYXIgbGVnZW5kCnBsb3QoMSwgdHlwZT0nbicsIGF4ZXM9RkFMU0UsIGFubj1GQUxTRSkKd2l0aChnc09uZURGLAogIGxlZ2VuZCgidG9wIiwgbGVnZW5kPXVuaXF1ZSh2b2xDYXRlZ29yeSksIGNvbD1hcy5pbnRlZ2VyKHVuaXF1ZSh2b2xDYXRlZ29yeSkpLCBwY2g9MTkpCikKYGBgCmBgYHtyIGZpZy53aWR0aD04fQpjbGVhcjNkKCkKd2l0aChnc09uZURGLHsKICAgICBwbG90M2QoeD14LCB5PXksIHo9eiwgdHlwZT0ncCcsIGNvbCA9IGFzLmludGVnZXIodm9sQ2F0ZWdvcnkpLCB5bGltPWMoLTQwMCwgNDAwKSkKfSkKcGxvdDNkKHJpbmcsIGFkZD1ULCBhbHBoYT0wLjIpCgp2aWV3M2QocGhpPTkwLCB0aGV0YT0tOTApCnJnbHdpZGdldCgpCmBgYApIb3cgbXVjaCBlbmVyZ3kgbG9zcyBhbmQgZGlzdGFuY2UgdHJhdmVsZWQgaW4gZWFjaCB2b2x1bWUgY2F0ZWdvcnkKYGBge3J9CmdzT25lREYgJT4lIGdyb3VwX2J5KHZvbENhdGVnb3J5KSAlPiUgc3VtbWFyaXplKHRvdGFsRGlzdF9tZXRlcnM9cm91bmQoc3VtKHN0ZXBMZW5ndGgpLzEwMDAsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxFTG9zc19NZVY9cm91bmQoc3VtKHRvdGFsRW5lcmd5RGVwb3NpdCksIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuU3RlcHMgPSBuKCkpCmBgYApPayAtIEkgYmVsaWV2ZSB0aGlzIChub3RlIHRoYXQgdGhlIHkgc2NhbGUgaXMgdmVyeSBzbWFsbCBjb21wYXJlZCB0byB0aGUgeCx6IHNjYWxlcyAtIHNvIHRoZSBtdW9uIGFjdXRhbGx5IHRyYXZlbHMgcXVpdGUgZmFyIGluIHggJiB6KS4gVGhlIG11b24gdHJhdmVscyBhYm91dCAyLjRtIGluIHRoZSBpcm9uIGFuZCByYW5nZXMgb3V0LiBQaHlzaWNzIChhbmQgR2VhbnQpIHdvcmshCgpMZXQncyB0cnkgYW5vdGhlciBvbmUKYGBge3J9CmdlYW50U3RlcE9uZUEgPC0gZ2VhbnRTdGVwUmVhZGVyQyhhcnRJbnB1dFRhZygnYXJ0ZzQ6S2VlcFN0ZXBzQWN0aW9uJyksIHR1cGxlKGFzLmludGVnZXIoMTApKSkKYGBgCgpgYGB7cn0KZ2V0R2FsbGVyeURhdGEoYXJyWHJkRmlsZXNbMV0sIGdlYW50U3RlcE9uZUEpCmBgYAoKYGBge3J9CmdzT25lQURGIDwtIGdhbGxlcnlSZWFkZXJfZGYoZ2VhbnRTdGVwT25lQSkKZ3NPbmVBREYgJT4lIG11dGF0ZShmaWxlRW50cnkgPSAxKSAlPiUKICBpbm5lcl9qb2luKHZvbE5hbWVERikgJT4lIAogIG11dGF0ZSh2b2xDYXRlZ29yeSA9IHZvbE5hbWUgJT4lIG1ha2VWb2xDYXRlZ29yeSAlPiUgYXMuZmFjdG9yKSU+JSAKICBzZWxlY3Qodm9sTmFtZSwgdm9sQ2F0ZWdvcnksIGV2ZXJ5dGhpbmcoKSkgLT4gZ3NPbmVBREYKZ3NPbmVBREYKYGBgCmBgYHtyfQpnc09uZUFERiAlPiUgZ3JvdXBfYnkodm9sQ2F0ZWdvcnkpICU+JSBzdW1tYXJpemUodG90YWxEaXN0X21ldGVycz1yb3VuZChzdW0oc3RlcExlbmd0aCkvMTAwMCwgMiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbEVMb3NzX01lVj1yb3VuZChzdW0odG90YWxFbmVyZ3lEZXBvc2l0KSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5TdGVwcyA9IG4oKSkgLT4gZ3NPbmVBREZTdW0KZ3NPbmVBREZTdW0KYGBgCkNoZWNrIHN1bXMKYGBge3J9CmdzT25lQURGU3VtICU+JSBzdW1tYXJpemUoc3VtKHRvdGFsRUxvc3NfTWVWKSkKYGBgCmBgYHtyfQpnc09uZUFERiAlPiUgc3VtbWFyaXplKHN1bSh0b3RhbEVuZXJneURlcG9zaXQpKQpgYGAKU2FuaXR5IGNoZWNrIHBhc3NlcyEKCjxhIG5hbWU9Im11b25TY2F0SW5mbCIvPgpgYGB7cn0KIyBsZW5nZW5kM2QgbG9va3MgdGVycmlibGUgLSBzbyBkbyBhIHJlZ3VsYXIgbGVnZW5kCnBsb3QoMSwgdHlwZT0nbicsIGF4ZXM9RkFMU0UsIGFubj1GQUxTRSkKd2l0aChnc09uZUFERiwKICBsZWdlbmQoInRvcCIsIGxlZ2VuZD11bmlxdWUodm9sQ2F0ZWdvcnkpLCBjb2w9YXMuaW50ZWdlcih1bmlxdWUodm9sQ2F0ZWdvcnkpKSwgcGNoPTE5KQopCmBgYAoKYGBge3J9CmNsZWFyM2QoKQp3aXRoKGdzT25lQURGLHsKICAgICBwbG90M2QoeD14LCB5PXksIHo9eiwgdHlwZT0ncCcsIGNvbCA9IGFzLmludGVnZXIodm9sQ2F0ZWdvcnkpLCB5bGltPWMoLTQwMCwgNDAwKSwgeGxpbT1jKC0xMDAwMCwgMTAwMDApLAogICAgICAgICAgICB6bGltPWMoLTEwMDAwLCAxMDAwMCkpCn0pCnBsb3QzZChyaW5nLCBhZGQ9VCwgYWxwaGE9MC4yKQoKdmlldzNkKHBoaT05MCwgdGhldGE9LTkwKQpyZ2x3aWRnZXQoKQpgYGAKVGhpcyBvbmUgc2NhdHRlcnMgaW4gdGhlIGluZmxlY3Rvci4gCgojIyBNdW9uIGRpc3RhbmNlIGZvciBtYW55IGV2ZW50cwoKQ2FuIHdlIHNob3cgaG93IGZhciBtdW9ucyBnbyBpbiB2YXJpb3VzIG1hdGVyaWFscz8KCkxldCdzIGNvbGxlY3QgZGF0YSBmb3IgMTAwIGV2ZW50cy4KCldlIGhhdmUgYSByZWFkZXIgdGhhdCBjYW4gZG8gKm4qIGV2ZW50cy4KYGBge3J9CnJlYWRfZmlsZSgnR2VhbnRUcmFja1JlYWRlck4ucHknKSAlPiUgY2F0CmBgYApgYGB7cn0KZ2VhbnRTdGVwUmVhZGVyTkMgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCdHZWFudFRyYWNrUmVhZGVyTi5weScpJEdlYW50U3RlcFJlYWRlck4KYGBgCmBgYHtyfQpnZWFudFN0ZXBOIDwtIGdlYW50U3RlcFJlYWRlck5DKGFydElucHV0VGFnKCdhcnRnNDpLZWVwU3RlcHNBY3Rpb24nKSwgMTAwMCwgMTAwMCkKYGBgCgpgYGB7cn0KZ2V0R2FsbGVyeURhdGEoYXJyWHJkRmlsZXNbMV0sIGdlYW50U3RlcE4pCmBgYApgYGB7cn0KZ3NOREYgPC0gZ2FsbGVyeVJlYWRlcl9kZihnZWFudFN0ZXBOKQpnc05ERiAlPiUgbXV0YXRlKGZpbGVFbnRyeSA9IDEsCiAgICAgICAgICAgICAgICAgcCA9IHNxcnQocHgqcHggKyBweSpweSArIHB6KnB6KSkgJT4lCiAgaW5uZXJfam9pbih2b2xOYW1lREYpICU+JSAKICBtdXRhdGUodm9sQ2F0ZWdvcnkgPSB2b2xOYW1lICU+JSBtYWtlVm9sQ2F0ZWdvcnkgJT4lIGFzLmZhY3RvciklPiUgCiAgc2VsZWN0KHZvbE5hbWUsIHZvbENhdGVnb3J5LCBwLCBldmVyeXRoaW5nKCkpIC0+IGdzTkRGCm5yb3coZ3NOREYpCmdzTkRGICU+JSBoZWFkKDEwMCkKYGBgCkxldCdzIGRvIGEgZGlzdHJpYnV0aW9uIG9mIGRpc3RhbmNlIGluIHRoZSBkaWZmZXJlbnQgdm9sdW1lcwpgYGB7cn0KZ3NOREYgJT4lIGdyb3VwX2J5KGV2ZW50RW50cnksIHZvbENhdGVnb3J5KSAlPiUgc3VtbWFyaXplKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbERpc3RfbWV0ZXJzPXJvdW5kKHN1bShzdGVwTGVuZ3RoKS8xMDAwLCAyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsRUxvc3NfTWVWPXJvdW5kKHN1bSh0b3RhbEVuZXJneURlcG9zaXQpLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgblN0ZXBzID0gbigpKSAtPiBnc05ERlN1bQpnc05ERlN1bQpgYGAKPGEgbmFtZT0iaGlzdGRpc3RyaW5nIi8+CmBgYHtyfQpnc05ERlN1bSAlPiUgZmlsdGVyKHZvbENhdGVnb3J5ID09ICdSaW5nJykgJT4lIAogIGdncGxvdChhZXMoeD10b3RhbERpc3RfbWV0ZXJzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTUwKSArIAogICAgbGFicyh4ID0gJ1RvdGFsIGRpc3RhbmNlIGluIFJpbmcgbWF0ZXJpYWwgKG0pJywgeT0nY291bnQnLCB0aXRsZT0nTXVvbiBpbiBSaW5nIE1hdGVyaWFsJykKYGBgCiBMZXQncyBsb29rIGF0IHRoaXMgYWNyb3NzIHRoZSB2b2x1bWUgY2F0ZWdvcmllcyBmb3IgZW5lcmd5IGxvc3MKIAogPGEgbmFtZT0iZW5lcmd5bG9zcyIvPgpgYGB7cn0KZ3NOREZTdW0gJT4lIGdncGxvdChhZXMoeD10b3RhbEVMb3NzX01lVikpICsgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKwogIGZhY2V0X3dyYXAofnZvbENhdGVnb3J5LCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgbGFicyh4ID0gJ1RvdGFsIGVuZXJneSBsb3NzIChNZVYpJywgeT0nY291bnQnLCB0aXRsZT0nRW5lcmd5IGxvc3MgaW4gbWF0ZXJpYWwnLAogICAgICAgc3VidGl0bGUgPSAnTm90ZSB0aGUgZGlmZmVyZW50IHNjYWxlcycpCmBgYApMZXQncyBnZXQgYW4gZW5lcmd5IGxvc3MgY3VydmUKYGBge3J9CmdzTkRGICU+JSBnZ3Bsb3QoYWVzKHg9cCwgeT10b3RhbEVuZXJneURlcG9zaXQpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofnZvbENhdGVnb3J5KSArIAogIGxhYnMoeD0iTXVvbiBNb21lbnR1bSAoTWVWL2MpIiwgeT0iVG90YWwgRW5lcmd5IGxvc3MgKE1lVikpIikKYGBgCldlbGwsIG5vdCBzdXJlIHdoYXQgYWxsIHRoaXMgbWVhbnMuIExldCdzIGxvb2sgZm9yIGEgcGFydGljdWxhciBldmVudC4uLgpgYGB7cn0KZ3NOREYgJT4lIGZpbHRlcihldmVudEVudHJ5ID09IDEzMDApICU+JSBnZ3Bsb3QoYWVzKHg9cCwgeT10b3RhbEVuZXJneURlcG9zaXQpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofnZvbENhdGVnb3J5KSArIAogIGxhYnMoeD0iTXVvbiBNb21lbnR1bSAoTWVWL2MpIiwgeT0iVG90YWwgRW5lcmd5IGxvc3MgKE1lVikpIiwgdGl0bGU9J0VuZXJneSBsb3NzIHZzLiBNb21lbnR1bSBmb3IgbXVvbnMgaW4gZXZlbnQgMTMwMCcgKQpgYGAKPGEgbmFtZT0ibnN0ZXBzIi8+SG93IG1hbnkgc3RlcHMgZG8gd2UgdGFrZT8KYGBge3J9CmdzTkRGU3VtICU+JSBnZ3Bsb3QoYWVzKHg9blN0ZXBzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTUwKSArCiAgZmFjZXRfd3JhcCh+dm9sQ2F0ZWdvcnksIHNjYWxlcyA9ICJmcmVlIikgKyAKICBsYWJzKHggPSAnTnVtYmVyIG9mIHN0ZXBzJywgeT0nY291bnQnLCB0aXRsZT0nTnVtYmVyIG9mIHN0ZXBzIGluIG1hdGVyaWFscycsCiAgICAgICBzdWJ0aXRsZSA9ICdOb3RlIHRoZSBkaWZmZXJlbnQgc2NhbGVzJykKYGBgCgpMZXQncyBsb29rIGF0IHR3byBldmVudHMKCiMjIyBNdW9uIHdpdGggbG9uZyBkaXN0YW5jZSBpbiBpcm9uCgpgYGB7cn0KZ3NOREZTdW0gJT4lIGZpbHRlcih2b2xDYXRlZ29yeSA9PSAnUmluZycsIHRvdGFsRGlzdF9tZXRlcnMgPiAzKQpgYGAKTGV0J3MgbG9vayBhdCBldmVudCAxNzU4CmBgYHtyfQpnc05ERiAlPiUgZmlsdGVyKGV2ZW50RW50cnkgPT0gMTc1OCkgLT4gZ3NOREYxNzU4CmdzTkRGMTc1OApgYGAKYGBge3J9CmdzTkRGMTc1OCAlPiUgZ3JvdXBfYnkoZXZlbnRFbnRyeSwgdm9sQ2F0ZWdvcnkpICU+JSBzdW1tYXJpemUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsRGlzdF9tZXRlcnM9cm91bmQoc3VtKHN0ZXBMZW5ndGgpLzEwMDAsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxFTG9zc19NZVY9cm91bmQoc3VtKHRvdGFsRW5lcmd5RGVwb3NpdCksIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuU3RlcHMgPSBuKCkpIC0+IGdzTkRGMTc1OFN1bQpnc05ERjE3NThTdW0KYGBgCkxldCdzIHBsb3QgaXQhCgpgYGB7cn0KZ3NOREYxNzU4ICU+JSBtdXRhdGUodm9sQ2F0ZWdvcnkgPSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKHZvbENhdGVnb3J5KSkpIC0+IGdzTkRGMTc1OApgYGAKCjxhIG5hbWU9Im11b25Jcm9uTG9uZyIvPgpgYGB7cn0KIyBsZW5nZW5kM2QgbG9va3MgdGVycmlibGUgLSBzbyBkbyBhIHJlZ3VsYXIgbGVnZW5kCnBsb3QoMCwgdHlwZT0nbicsIGF4ZXM9RkFMU0UsIGFubj1GQUxTRSkKd2l0aChnc05ERjE3NTgsCiAgbGVnZW5kKCJib3R0b20iLCBsZWdlbmQ9dW5pcXVlKHZvbENhdGVnb3J5KSwgY29sPWFzLmludGVnZXIodW5pcXVlKHZvbENhdGVnb3J5KSksIHBjaD0xOSkKKQpgYGAKCmBgYHtyfQpjbGVhcjNkKCkKd2l0aChnc05ERjE3NTgsewogICAgIHBsb3QzZCh4PXgsIHk9eSwgej16LCB0eXBlPSdwJywgY29sID0gYXMuaW50ZWdlcih2b2xDYXRlZ29yeSksIHlsaW09YygtNDAwLCA0MDApLCB4bGltPWMoLTEwMDAwLCAxMDAwMCksCiAgICAgICAgICAgIHpsaW09YygtMTAwMDAsIDEwMDAwKSkKfSkKcGxvdDNkKHJpbmcsIGFkZD1ULCBhbHBoYT0wLjIpCnZpZXczZChwaGk9OTAsIHRoZXRhPS05MCkKcmdsd2lkZ2V0KCkKYGBgCkxvb2tzIGxpa2UgdGhpcyBvbmUgc3RhcnRlZCBvdXQgaW4gYSBzdHJhbmdlIHBsYWNlLiAKCiMjIyBNdW9uIHdpdGggbWFueSB0dXJucwoKYGBge3J9CmdzTkRGU3VtICU+JSBmaWx0ZXIodm9sQ2F0ZWdvcnkgPT0gJ0FyYycsIHRvdGFsRGlzdF9tZXRlcnMgPiA4NzApCmBgYApMZXQncyB0cnkgZXZlbnQgMTUwOQoKYGBge3J9CmdzTkRGICU+JSBmaWx0ZXIoZXZlbnRFbnRyeSA9PSAxNTA5KSAtPiBnc05ERjE1MDkKZ3NOREYxNTA5CmBgYApgYGB7cn0KZ3NOREYxNTA5ICU+JSBncm91cF9ieShldmVudEVudHJ5LCB2b2xDYXRlZ29yeSkgJT4lIHN1bW1hcml6ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxEaXN0X21ldGVycz1yb3VuZChzdW0oc3RlcExlbmd0aCkvMTAwMCwgMiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbEVMb3NzX01lVj1yb3VuZChzdW0odG90YWxFbmVyZ3lEZXBvc2l0KSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5TdGVwcyA9IG4oKSkgLT4gZ3NOREYxNTA5U3VtCmdzTkRGMTUwOVN1bQpgYGAKTGV0J3MgcGxvdCBpdCEKCmBgYHtyfQpnc05ERjE1MDkgJT4lIG11dGF0ZSh2b2xDYXRlZ29yeSA9IGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIodm9sQ2F0ZWdvcnkpKSkgLT4gZ3NOREYxNTA5CmBgYAoKPGEgbmFtZT0ibG9uZ011b24iLz4KYGBge3J9CiMgbGVuZ2VuZDNkIGxvb2tzIHRlcnJpYmxlIC0gc28gZG8gYSByZWd1bGFyIGxlZ2VuZApwbG90KDAsIHR5cGU9J24nLCBheGVzPUZBTFNFLCBhbm49RkFMU0UpCndpdGgoZ3NOREYxNTA5LAogIGxlZ2VuZCgiYm90dG9tIiwgbGVnZW5kPXVuaXF1ZSh2b2xDYXRlZ29yeSksIGNvbD1hcy5pbnRlZ2VyKHVuaXF1ZSh2b2xDYXRlZ29yeSkpLCBwY2g9MTkpCikKYGBgCgpgYGB7cn0KY2xlYXIzZCgpCndpdGgoZ3NOREYxNTA5LHsKICAgICBwbG90M2QoeD14LCB5PXksIHo9eiwgdHlwZT0ncCcsIGNvbCA9IGFzLmludGVnZXIodm9sQ2F0ZWdvcnkpLCB5bGltPWMoLTQwMCwgNDAwKSwgeGxpbT1jKC0xMDAwMCwgMTAwMDApLAogICAgICAgICAgICB6bGltPWMoLTEwMDAwLCAxMDAwMCkpCn0pCnBsb3QzZChyaW5nLCBhZGQ9VCwgYWxwaGE9MC4yKQp2aWV3M2QocGhpPTkwLCB0aGV0YT0tOTApCnJnbHdpZGdldCgpCmBgYApMb29rcyBsaWtlIGEgbmljZSBzdG9yZWQgbXVvbiEhIENhbiB3ZSBzZWUgQ0JPPwoKYGBge3J9CmdzTkRGMTUwOSAlPiUgbXV0YXRlKHIgPSBzcXJ0KHgqeCt6KnopKSAtPiBnc05ERjE1MDkKYGBgCgo8YSBuYW1lPSJjYm8iLz4KYGBge3J9CnAxIDwtIGdzTkRGMTUwOSAlPiUgZmlsdGVyKHZvbENhdGVnb3J5ICVpbiUgYygnUm5nVHJrUGxhbmUnKSkgJT4lIGdncGxvdChhZXMoeD1nbG9iYWxUaW1lLCB5PXIpKSArIGdlb21fbGluZSgpICsKICBsYWJzKHg9J0dsb2JhbCB0aW1lIChucyknLCB5PSdSYWRpdXMgKG1tKScsIHRpdGxlPSdSYWRpYWwgQ0JPJywgc3VidGl0bGU9Ik1lYXN1cmVkIGJ5IFJpbmcgVHJhY2tpbmcgUGxhbmVzIikKIwpwMiA8LSBnc05ERjE1MDkgJT4lIGZpbHRlcih2b2xDYXRlZ29yeSAlaW4lIGMoJ0FyYycsJ1JuZ1Rya1BsYW5lJykpICU+JSBnZ3Bsb3QoYWVzKHg9Z2xvYmFsVGltZSwgeT1yKSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGxhYnMoeD0nR2xvYmFsIHRpbWUgKG5zKScsIHk9J1JhZGl1cyAobW0pJywgc3VidGl0bGU9Ik1lYXN1cmVkIGJ5IEFyYyBoaXRzIGFuZCBSaW5nIFRyYWNraW5nIFBsYW5lcyIpCiMKZ3JpZC5hcnJhbmdlKHAxLCBwMikKYGBgCldlIGNhbiBldmVuIHNlZSB0aGUga2ljayEKCkhlcmUgd2UnbGwgcGxvdCB0aGUgcG9pbnRzIHRvby4gCmBgYHtyfQpwMSA8LSBnc05ERjE1MDkgJT4lIGZpbHRlcih2b2xDYXRlZ29yeSAlaW4lIGMoJ1JuZ1Rya1BsYW5lJykpICU+JSBnZ3Bsb3QoYWVzKHg9Z2xvYmFsVGltZSwgeT15KSkgKyBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHg9J0dsb2JhbCB0aW1lIChucyknLCB5PSd5IChtbSknLCB0aXRsZT0nVmVydGljYWwgQ0JPJywgc3VidGl0bGU9Ik1lYXN1cmVkIGJ5IFJpbmcgVHJhY2tpbmcgUGxhbmVzIikKIwpwMiA8LSBnc05ERjE1MDkgJT4lIGZpbHRlcih2b2xDYXRlZ29yeSAlaW4lIGMoJ0FyYycsJ1JuZ1Rya1BsYW5lJykpICU+JSBnZ3Bsb3QoYWVzKHg9Z2xvYmFsVGltZSwgeT15KSkgKyAKICBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKSArIAogIGxhYnMoeD0nR2xvYmFsIHRpbWUgKG5zKScsIHk9J3kgKG1tKScsIHN1YnRpdGxlPSJNZWFzdXJlZCBieSBBcmMgaGl0cyBhbmQgUmluZyBUcmFja2luZyBQbGFuZXMiKQojCmdyaWQuYXJyYW5nZShwMSwgcDIpCmBgYAojICJHaG9zdCIgY3lsaW5kZXIgYXQgdmFjdXVtIGNoYW1iZXIKCldlIHdhbnQgdG8gZXhhbWluZSBtdW9uIGVzY2FwZWVzIHRoYXQgbGVhdmUgdGhlIHZhY3V1bSBjaGFtYmVyLiBUbyBtZWFzdXJlIHRoaXMgZmVhdHVyZSwgd2UgaGF2ZSBhIGdob3N0IGN5bGluZGVyIGF0IHRoZSBvdXRlciB3YWxsIG9mIHRoZSB2YWN1dW0gY2hhbWJlciBhcyB3ZWxsIGFzIGFib3ZlIGFuZCBiZWxvdyB0aGUgc3RvcmFnZSByZWdpb24gKGJlZm9yZSB0aGUgbXVvbiB3b3VsZCBoaXQgaXJvbikuIFRoYXQgc2hvdWxkIGNhdGNoIGV2ZXJ5IG11b24gdGhhdCBkb2Vzbid0IGdldCBzdG9yZWQgYW5kIG1ha2VzIGl0IG91dCBvZiB0aGUgc3RvcmFnZSByZWdpb24uIFRoZSBkYXRhIGlzIGBnbTJ0cnV0aDo6R2hvc3REZXRlY3RvckFydFJlY29yZHNfYXJ0ZzRfR2hvc3RDeWxpbmRlckRldGVjdG9yYC4KClRoZXJlIGlzIGFsc28gYSBnaG9zdCBjeWxpbmRlciBqdXN0IG9uIHRoZSBpbnNpZGUgb2YgdGhlIHdvcmxkIGN1YmUgdG8gc2VlIG11b25zIHRoYXQgd291bGQgYWN0dWFsbHkgbGVhdmUgdGhlIHJpbmcgKGFuZCBtYWtlIGl0IHRocm91Z2ggdGhlIGlyb24pLiBUaGUgZGF0YSBpcyBgZ20ydHJ1dGg6Okdob3N0RGV0ZWN0b3JBcnRSZWNvcmRzX2FydGc0X0dob3N0TmVhcldvcmxkRGV0ZWN0b3JgLiAKCiMjIExvYWQgdGhlIEdob3N0IGN5bGluZGVyIGRhdGEKClNlZSBbZ20yZGF0YXByb2R1Y3RzL21jL2dob3N0ZGV0ZWN0b3JzL0dob3N0RGV0ZWN0b3JBcnRSZWNvcmQuaGhdKGh0dHBzOi8vY2RjdnMuZm5hbC5nb3YvcmVkbWluZS9wcm9qZWN0cy9nbTJkYXRhcHJvZHVjdHMvcmVwb3NpdG9yeS9lbnRyeS9tYy9naG9zdGRldGVjdG9ycy9HaG9zdERldGVjdG9yQXJ0UmVjb3JkLmhoP3Jldj1mZWF0dXJlJTJGbWRjMikgLgoKYGBgcgpyZWFkZXJDbGFzc1NrZWwoJ2dtMnRydXRoOjpHaG9zdERldGVjdG9yQXJ0UmVjb3JkJywgd3JpdGVGaWxlID0gJ0dob3N0RGV0ZWN0b3JSZWFkZXIucHknKQpgYGAKYGBge3J9CnJlYWRfZmlsZSgnR2hvc3REZXRlY3RvclJlYWRlci5weScpICU+JSBjYXQKYGBgCgoKYGBge3J9Cmdob3N0RGV0ZWN0b3JSZWFkZXJDIDwtIGNyZWF0ZVJlYWRlckNsYXNzX2Zyb21fZmlsZSgnR2hvc3REZXRlY3RvclJlYWRlci5weScpJEdob3N0RGV0ZWN0b3JBcnRSZWNvcmRSZWFkZXIKYGBgCgpMZXQncyByZWFkIGJvdGggZ2hvc3QgY3lsaW5kZXJzCmBgYHtyfQpnaEN5bFJlYWRlciA8LSBnaG9zdERldGVjdG9yUmVhZGVyQyhhcnRJbnB1dFRhZygnYXJ0ZzQ6R2hvc3RDeWxpbmRlckRldGVjdG9yJykpCmdoV2xkUmVhZGVyIDwtIGdob3N0RGV0ZWN0b3JSZWFkZXJDKGFydElucHV0VGFnKCdhcnRnNDpHaG9zdE5lYXJXb3JsZERldGVjdG9yJykpCmBgYAoKTG9hZCBmb3Igb25lIGZpbGUKYGBge3J9CmdldEdhbGxlcnlEYXRhKGFyclhyZEZpbGVzLCBjKGdoQ3lsUmVhZGVyLCBnaFdsZFJlYWRlcikpCmBgYApMb2FkIHRoZSBkYXRhIGludG8gUgoKYGBge3J9CmdoQ3lsREYgPC0gZ2FsbGVyeVJlYWRlcl9kZihnaEN5bFJlYWRlcikKZ2hXbGRERiA8LSBnYWxsZXJ5UmVhZGVyX2RmKGdoV2xkUmVhZGVyKQpgYGAKCkxvb2sgYXQgd2hhdCB3ZSBnb3QKYGBge3J9Cm5yb3coZ2hDeWxERikgJT4lIGZvcm1hdChiaWcubWFyaz0iLCIpCm5yb3coZ2hXbGRERikgJT4lIGZvcm1hdChiaWcubWFyaz0iLCIpCmBgYAoKYGBge3J9CmdoQ3lsREYgJT4lIGhlYWQoMTAwKQpgYGAKYGBge3J9CmdoV2xkREYgJT4lIGhlYWQoMTAwKQpgYGAKYGBge3J9CndyaXRlX3JkcyhnaEN5bERGLCAnZ2hDeWxERi5yZHMnKSAgIyBXcml0ZSBvdXQganVzdCBpbiBjYXNlLCBzaW5jZSB0aGVzZSBhcmUgZXhwZW5zaXZlIHRvIGV4dHJhY3QKd3JpdGVfcmRzKGdoV2xkREYsICdnaFdsZERGLnJkcycpCmBgYAoKRmlsbCBpbiB0aGUgc2NlbmFyaW8gYW5kIHBkZwpgYGB7cn0KcGRncyA8LSBjKCdlLSc9IDExLCAnbnVfZSc9IDEyLCAnbXUtJz0gMTMsICdudV9tdSc9IDE0LCAndGF1LSc9IDE1LCAnbnVfdGF1Jz0gMTYsCiAgICAgICAgICAnZSsnPS0xMSwgJ2FudGlfbnVfZSc9LTEyLCAnbXUrJz0tMTMsICdhbnRpX251X211Jz0tMTQsICd0YXUrJz0tMTUsICdhbnRpX251X3RhdSc9LTE2LAogICAgICAgICAgJ2dhbSc9MjIsICdwJz0yMjEyLCAnbic9MjExMiwgJ2FudGktcCc9LTIyMTIsICdhbnRpLW4nPS0yMTEyKQpwZGdzCmBgYAoKYGBge3J9CiMgV2UgbmVlZCB0byBiZSBhIGxpdHRsZSBjYXJlZnVsLCBzaW5jZSB0aGUgc2NlbmFyaW8gbmFtZXMgYXJlIHJlcGVhdGVkICh3ZSBoYXZlIHR3byBmaWxlcyBwZXIgc2NlbmFyaW8pLiAKIyBXZSBjYW4ndCBtYWtlIGZhY3RvciBsZXZlbHMgb3V0IG9mIHRoYXQuIAojIFRoZSBmaWxlIGVudHJpZXMgd2l0aCAwLTUgYXJlIGVhc3kuIDYtMTEgYXJlIGhhcmRlcgphZGRTY2VuYXJpb0ZhY3RvciA8LSBmdW5jdGlvbihmKSBmYWN0b3IoaWZfZWxzZShmID49IDYsIGFzLmludGVnZXIoZi02KSwgZiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPTA6KGxlbmd0aCh1bmlxdWUoc2NlbmFyaW9zKSktMSksIGxhYmVscz11bmlxdWUoc2NlbmFyaW9zKSkKIwptdXRhdGVUaGlzIDwtIGZ1bmN0aW9uKGRmKSBtdXRhdGUoZGYsIAogICAgICAgICAgICAgICAgICAgICAgIHNjZW5hcmlvID0gYWRkU2NlbmFyaW9GYWN0b3IoZmlsZUVudHJ5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBkZyA9IGZhY3RvcihwYXJ0aWNsZUlELCBsZXZlbHM9cGRncywgbGFiZWxzPW5hbWVzKHBkZ3MpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgciA9IHNxcnQoeCp4K3oqeiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaGkgPSBhdGFuMih6LCB4KSAlPiUgaWZfZWxzZSguID4gMCwgLiwgMipwaSsgLikgKiAoMzYwLjAvKDIqcGkpKSApCiMKZ2hDeWxERiAlPiUgbXV0YXRlVGhpcyAtPiBnaEN5bERGCiMKZ2hXbGRERiAlPiUgbXV0YXRlVGhpcyAtPiBnaFdsZERGCmBgYAoKIyMgTG9zc2VzIGRldGVjdGVkIGJ5IHRoZSBjeWxpbmRlciBnaG9zdCBkZXRlY3RvcgoKVGhlICJnaG9zdCBjeWxpbmRlciIgdmlydHVhbCBkZXRlY3RvciBpbiB0aGUgcGFyYWxsZWwgd29ybGQgc2VlcyBtdW9uIGVzY2FwZWVzIHRoYXQgaGl0IHRoZSB2YWN1dW0gY2hhbWJlciBvdXRlciB3YWxsLiBIb3cgbWFueSBoaXRzIHBlciBtdW9uIGRvIHdlIHNlZT8gCgpQdWxsIG91dCBqdXN0IHRoZSBtdW9uCmBgYHtyfQpnaEN5bERGICU+JSBmaWx0ZXIodHJhY2tJRCA9PSAxKSAtPiBnaEN5bE11REYKbnJvdyhnaEN5bE11REYpICU+JSBmb3JtYXQoYmlnLm1hcms9JywnKQpgYGAKVG8gZG8gdGhpcyByaWdodCwgd2UgbmVlZCB0byBtYWtlIGEgImtleSIgb3V0IG9mIHRoZSBmaWxlRW50cnkgYW5kIGV2ZW50RW50cnkgY29sdW1ucy4gCgpgYGB7cn0KZ2hDeWxNdURGICU+JSBtdXRhdGUoIGZsZXZLZXkgPSB1bml0ZSAuLi4gICkKYGBgCgpgYGB7cn0KZ2hDeWxNdURGICU+JSBncm91cF9ieShmaWxlRW50cnksIGV2ZW50RW50cnkpICU+JSB0YWxseSgpCmBgYAoKCkkndmUgbWFkZSAzRCBwbG90cyBvZiBhbGwgdGhlIHNjZW5hcmlvcyAoc2VlIGJlbG93KS4gSW50ZXJlc3RpbmcgdG8gc2VlIHdoYXQgZnJhY3Rpb24gZ2V0IGtpY2tlZCB1cCBhbmQgZG93biAoYXJlIHRoZSBnaG9zdCBoaXRzIG9uIHRoZSAiY2VpbGluZyBvciBmbG9vciIgb3Igb24gdGhlICJ3YWxsIikuIFdlIGNhbiB0ZWxsIGJ5IHBsb3R0aW5nIHRoZSAqeSogY29vcmRpbmF0ZS4uLgoKYGBge3J9CmdoQ3lsREYgJT4lIAogIGdncGxvdChhZXMoeSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKyBzY2FsZV95X2xvZzEwKCkgKyBmYWNldF93cmFwKH5zY2VuYXJpbykKYGBgCgoKIyMjIDNEIHBsb3RzIG9mIGFsbCBzY2VuYXJpb3MKCkxldCdzIHByYWN0aWNlIHdpdGgganVzdCB0aGUgKmV2ZXJ5dGhpbmcqIHNjZW5hcmlvCmBgYHtyfQpnaEN5bE11X2V2ZXJ5dGhpbmdfREYgPC0gZ2hDeWxNdURGICU+JSBmaWx0ZXIoc2NlbmFyaW8gPT0gJ2V2ZXJ5dGhpbmcnKQpucm93KGdoQ3lsTXVfZXZlcnl0aGluZ19ERikgJT4lIGZvcm1hdChiaWcubWFyaz0nLCcpCmBgYApMZXQncyBwbG90IHdoZXJlIHRoZXNlIGFyZQoKYGBge3J9CnBsb3RFc2NhcGVlcyA8LSBmdW5jdGlvbihpKSB7CiAgd2l0aCggZ2hDeWxNdURGICU+JSBmaWx0ZXIoc2NlbmFyaW8gPT0gc2NlbmFyaW9zW2ldKSwKICAgcGxvdDNkKHg9eCwgeT15LCB6PXosIHhsaW09YygtODAwMCwgODAwMCksIHlsaW09YygtMTIwLCAxMjApLCB6bGltPWMoLTgwMDAsIDgwMDApLCBtYWluPXNjZW5hcmlvc1tpXSkpCiAgcGxvdDNkKHJpbmcsIGFkZD1ULCBhbHBoYT0wLjIpCiAgdmlldzNkKHBoaT05MCwgdGhldGE9LTkwKQp9CmBgYAoKYGBge3J9CmNsZWFyM2QoKQpwbG90RXNjYXBlZXMoMSkKcmdsd2lkZ2V0KCkKYGBgClRoZXkncmUgYWxsIGxvc3QgdXAgb3IgZG93biwgbm90IGFsb25nIHRoZSBzaWRlLiBMZXQncyB0cnkgbW9yZSBvZiB0aGUgc2NlbmFyaW9zLgpgYGB7cn0KY2xlYXIzZCgpCnBsb3RFc2NhcGVlcygyKQpyZ2x3aWRnZXQoKQpgYGAKTm8gc3RvcmFnZSByaW5nIGRpcG9sZSBmaWVsZDogQXMgd2UgZXhwZWN0LCB0aGUgbXVvbnMgZG9uJ3QgZ28gdmVyeSBmYXIuIApgYGB7cn0KY2xlYXIzZCgpCnBsb3RFc2NhcGVlcygzKQpyZ2x3aWRnZXQoKQpgYGAKTm8gaW5mbGVjdG9yIGZpZWxkOiBTZWVtaW5nbHkgbG90cyBvZiBtdW9ucyBraWNrZWQgdXAgYW5kIGRvd24gKG5lZWQgdG8gY2hlY2sgdGhhdCkuIE1hbnkgc2xhbSBpbnRvIHRoZSB3YWxsLiAKCmBgYHtyfQpjbGVhcjNkKCkKcGxvdEVzY2FwZWVzKDQpCnJnbHdpZGdldCgpCmBgYAoKYGBge3J9CmNsZWFyM2QoKQpwbG90RXNjYXBlZXMoNSkKcmdsd2lkZ2V0KCkKYGBgCgpgYGB7cn0KY2xlYXIzZCgpCnBsb3RFc2NhcGVlcyg2KQpyZ2x3aWRnZXQoKQpgYGAKV2VsbCwgdGhpcyBpc24ndCB0b28gaWxsdW1pbmF0aW5nLiA=